개요
블록체인 서비스에서 사용자가 겪는 가장 큰 장벽 중 하나는 단연 가스비(Gas Fee)임 아무리 좋은 서비스를 만들어도, 사용자가 지갑에 가스비로 쓸 코인(ETH 등)을 보유하고 있어야 한다는 점은 대중화를 가로막는 결정적인 요인임 이 문제를 해결해 사용자가 가스비 걱정 없이 서비스 핵심 가치에만 집중하게 만드는 것, 즉 가스리스 트랜잭션(Gasless Transaction)을 구현하는 것이 이번 개발의 최종 목표였음
zkSync Era는 이를 위해 페이마스터(Paymaster)라는 강력한 시스템을 제공함. 페이마스터는 서비스 제공자 같은 제3자가 사용자를 대신해 트랜잭션 수수료를 지불할 수 있게 해주는 스마트 컨트랙트임
문제 정의: 단순 설계 페이마스터의 명확한 한계
가장 먼저 검토한 toAddressPaymaster는 특정 컨트랙트로 향하는 트랜잭션의 가스비만 대납해주는 단순한 모델임. 하지만 이 방식은 두 가지 치명적인 허점을 가짐
의도치 않은 트랜잭션 허용: 신규 사용자에게 지갑을 만들어주는 AccountFactory 컨트랙트 주소를 페이마스터에 등록할 경우, 우리 서비스와 무관한 모든 사용자가 이 팩토리를 사용할 때 발생하는 가스비를 우리가 전부 대납하게 됨. 페이마스터가 의도치 않게 공공재가 되어버리는 상황
가스비 소모 공격에 취약: gasLimit을 검증하는 로직이 없어, 악의적 사용자가 가스비를 비정상적으로 높게 책정해 트랜잭션 한 번으로 페이마스터의 모든 자금을 고갈시킬 수 있는 위험이 존재함
결론적으로 우리가 허용한 사용자가 우리가 의도한 작업을 할 때 발생하는 합리적인 수준의 가스비만을 선별적으로 대납할 똑똑한 방법이 필요했음
해결의 실마리: Off-chain Signature Verification
이 문제의 해결책으로 오프체인 서명 검증(Off-chain Signature Verification) 패턴을 도입하기로 결정함
이 패턴은 블록체인 외부(서버)에서 생성된 디지털 서명을 블록체인 위(스마트 컨트랙트)에서 검증하는 방식임. 즉, 가스비 대납 여부를 컨트랙트가 단독으로 결정하는 게 아니라, 우리가 통제하는 서버의 ‘허가’를 받아 결정하게 만드는 것
이 과정에서 다음과 같은 주요 개념들이 활용됨
메타 트랜잭션(Meta-transaction): 사용자가 직접 트랜잭션을 생성하고 가스비를 지불하는 대신, 트랜잭션의 내용이 될 데이터에 서명만 함. 그러면 이 서명을 받은 대리인(Relayer)이 실제 트랜잭션으로 만들어 수수료를 대신 내고 블록체인에 제출하는 패턴. 우리가 구현할 페이마스터는 zkSync 시스템이 릴레이어 역할을 해주는 메타 트랜잭션의 일종임
EIP-712: 단순 텍스트가 아닌, json처럼 구조화된 데이터에 대한 서명 표준. 사용자가 무엇에 서명하는지 명확히 인지할 수 있고, 다른 컨트랙트나 체인에서 서명을 재사용하는 ‘재생 공격(Replay Attack)‘을 방지하는 도메인 분리(Domain Separator) 기능을 포함하고 있어 페이마스터 서명에 필수적임
구현 과정: 서명 기반 페이마스터 설계 및 개발
전체 흐름은 서버의 서명 생성(Off-chain)과 컨트랙트의 서명 검증(On-chain) 두 단계로 나뉨
1. 서버 (Off-chain): 서명 생성 단계
사용자가 트랜잭션을 실행하기 직전, 클라이언트는 트랜잭션 데이터를 백엔드 서버로 전송함. 서버는 다음 절차를 수행
요청 검증: 요청을 보낸 사용자가 우리 서비스의 유효한 사용자인지, 트랜잭션 내용이 비즈니스 로직에 부합하는지 등을 검증
가스비 추정 및 파라미터 설정: 트랜잭션에 필요한 가스비를 추정하고, 여기에 약간의 버퍼를 더해 recommendedGasLimit과 recommendedGasPrice를 결정. 서명이 유효할 시간(expiryTimestamp)도 설정함
EIP-712 서명 생성: 검증된 파라미터들을 바탕으로 EIP-712 표준에 따라 구조화된 데이터를 만들고, 서버만 안전하게 보관하고 있는 개인키로 이 데이터에 서명함
// 서버에서 EIP-712 타입에 맞춰 서명할 데이터를 구성
const domain = {
name: "SignatureBasedPaymaster",
version: "1",
chainId: chainId,
verifyingContract: PAYMASTER_ADDRESS
};
const types = {
SignatureBasedPaymaster: [
{ name: 'userAddress', type: 'address' },
{ name: 'expiryTimestamp', type: 'uint256' },
{ name: 'recommendedGasLimit', type: 'uint256' },
{ name: 'recommendedGasPrice', type: 'uint256' }
]
};
const value = { /* userAddress, expiryTimestamp 등 실제 값 */ };
// 서버의 개인키로 서명 생성
const signature = await signerWallet.signTypedData(domain, types, value);
이 서명값과 관련 파라미터들을 클라이언트에 반환하면, 클라이언트는 이 데이터를 트랜잭션과 함께 zkSync 네트워크로 전송함
2. 컨트랙트 (On-chain): 서명 검증 단계
사용자의 트랜잭션은 zkSync 부트로더(Bootloader)를 통해 페이마스터 컨트랙트의 validateAndPayForPaymasterTransaction 함수를 호출함. 이 함수는 서명 검증의 핵심임
데이터 재구성: 트랜잭션에 포함된 userAddress, expiryTimestamp, recommendedGasLimit 등의 파라미터를 가져와 서버가 서명했던 것과 완벽히 동일한 구조와 순서로 해시(hash)를 다시 만듦
서명자 복원: 재구성한 해시와 트랜잭션에 포함된 서명(signature)을 이용해 암호학적 함수(ecrecover)를 호출. 이를 통해 이 서명을 생성한 주소를 역으로 계산해냄
서명자 검증: 복원된 주소가 컨트랙트에 미리 저장해 둔 우리 서버의 주소(signer)와 일치하는지 확인. 일치해야만 서버가 허가한 유효한 요청으로 간주함
만약 누군가 서버로부터 받은 서명을 그대로 두고 파라미터(ex: 가스비)를 변경하려 시도하면, 1번 단계에서 생성되는 해시값이 달라져 3번 검증 단계에서 실패하게 됨. 이를 통해 데이터의 무결성과 서명자의 신뢰성을 동시에 확보함
// 1. 서버가 서명했을 데이터의 해시를 컨트랙트에서 동일하게 재구성
bytes32 digest = hashTypedDataV4(keccak256(abi.encode(
SIGNATURE_TYPEHASH,
userAddress,
expiryTimestamp,
recommendedGasLimit,
recommendedGasPrice
)));
// 2. 서명과 재구성된 해시를 사용해 서명자 주소를 복원
address recoveredSigner = digest.recover(signature);
// 3. 복원된 주소가 우리가 신뢰하는 서버의 주소(signer)와 일치하는지 확인
require(
signer == recoveredSigner,
"Paymaster: Invalid signer"
);
// ... 검증 통과 후 가스비 대납 로직 실행
결론 및 회고
zkSync의 페이마스터는 사용자의 가스비를 대신 지불하는 기능을 손쉽게 구현할 수 있는 강력한 기반을 제공함 개발자는 이를 통해 사용자의 진입 장벽을 낮추고 UX를 크게 개선할 수 있음
하지만 여기에 오프체인 서명 검증(Off-chain Signature Verification) 패턴을 결합함으로써, 단순한 가스비 대납을 넘어 정교한 권한 제어가 가능해짐 서버에서 각 요청을 사전에 검증하고 서명을 발급하는 방식은 다음과 같은 명확한 이점을 가져왔음
운영 안정성: 누가, 언제, 어떤 트랜잭션의 가스비를 지원받을지 서버단에서 유연하게 제어할 수 있어, 무분별한 비용 소모를 막고 예측 가능한 운영이 가능
보안 강화: 허가된 주체만이 서명을 통해 가스비를 사용할 수 있으므로, 초기 모델의 가장 큰 문제였던 자금 고갈 공격과 의도치 않은 트랜잭션 허용을 원천적으로 차단할 수 있었음
결론적으로, zkSync의 편리한 온체인 기능과 서버의 오프체인 로직을 결합한 이 접근법은 보안과 운영 안정성을 모두 확보하는 효과적인 해결책이었음 이를 통해 시스템의 자산을 안전하게 보호하며 지속 가능한 서비스를 운영할 수 있는 견고한 토대를 마련하게 되었음