EIP-8182 비공개 ETH·ERC-20 전송 (Private ETH and ERC-20 Transfers)

Hegota 포함 제외 (DFI) · 보안·프라이버시 · 원문 상태 Review · 2026-03-03 작성

프로토콜이 직접 관리하는 쉴드 풀(shielded pool) 시스템 컨트랙트를 설치해 ETH와 ERC-20 토큰을 비공개로 보낼 수 있게 합니다. 새 명령어나 트랜잭션 타입은 추가하지 않습니다.

왜 필요한가

이더리움에서 자산을 공개로 보내는 일은 쉽습니다. 하지만 보내는 사람, 받는 사람, 금액을 숨겨야 하는 전송에는 공통 기본값이 없습니다. 급여 지급, 기업 자금 관리, 기부 같은 평범한 금융 활동은 내역이 모두에게 보이면 곤란해서 지금은 전통 금융이나 다른 블록체인으로 밀려납니다. 원문은 시장이 이 문제를 스스로 풀지 못한 이유를 풀의 규모 효과에서 찾습니다. 비공개 전송의 익명성은 같은 풀을 쓰는 사용자와 자금이 얼마나 많은지에 달려 있어 작은 풀은 제품이 아무리 좋아도 프라이버시가 약합니다. 또 풀을 업그레이드할 수 있게 만들면 그 권한을 가진 쪽이 사용자 자금을 위협할 수 있고 반대로 불변으로 만들면 증명 체계가 약해져도 고칠 수 없습니다. 이 EIP는 하드포크로만 바뀌는 프로토콜 관리 풀을 제공해 이 교착을 깨자고 제안합니다.

무엇이 바뀌나

지금

EIP-8182 이후

누구에게 영향이 있나

심층 설명

시스템 컨트랙트와 업그레이드 모델

쉴드 풀은 SHIELDED_POOL_ADDRESS = 0x0000000000000000000000000000000000081820에 시스템 컨트랙트로 배포됩니다. 프록시, 관리자 함수, 온체인 업그레이드 경로가 전혀 없고 코드는 이후 하드포크의 상태 전이 규칙으로만 교체할 수 있으며 스토리지는 교체 후에도 유지됩니다. 풀 증명 검증 키는 EIP-4844의 KZG와 같은 패턴인 일회성 다자간 신뢰 설정(trusted setup) 세리머니로 확정되고 컨트랙트 바이트코드에 내장됩니다. 세리머니가 끝나야 바이트코드가 확정되므로 EIP가 특정 바이트코드를 못 박지 않습니다.

분할 증명 구조: 풀 증명과 인증 증명

노트를 쓰는 transact는 신뢰 성격이 다른 두 증명으로 나뉩니다. 풀 증명은 하드포크로만 바뀌는 단일 회로의 Groth16 BN254 SNARK로, 가치 보존, 널리파이어 유도, 머클 멤버십, 출력 노트 시크릿의 결정적 유도, 레지스트리 리프·정책 집합 멤버십, 의도 다이제스트 재계산 같은 프로토콜 불변식을 모두 책임집니다. 인증 증명은 누구나 회로와 authVerifier 컨트랙트를 만들어 배포할 수 있는 무허가 계층으로, 사용자 자격 검증과 의도 파싱을 맡아 [blindedAuthCommitment, transactionIntentDigest] 두 값을 출력합니다. 시스템 컨트랙트는 풀 증명을 내장 검증 키로 검증하고 인증 증명은 사용자가 고른 authVerifier에 staticcall로 넘깁니다. 풀 회로의 버그는 풀 전체 자금을 위협하지만 인증 회로의 버그는 그 검증자에 정책을 등록한 신원만 위협합니다.

노트, 널리파이어, 고정 형태 트랜잭션

노트는 poseidon 해시 사슬로 커밋됩니다. ownerCommitment = poseidon(OWNER_COMMITMENT_DOMAIN, ownerNullifierKeyHash, noteSecret), noteBodyCommitment는 여기에 amount와 tokenAddress를 묶고 최종 noteCommitment는 컨트랙트가 부여한 leafIndex를 더해 구조적 유일성을 만듭니다. 노트를 쓰면 nullifier = poseidon(NULLIFIER_DOMAIN, noteCommitment, ownerNullifierKey)가 공개돼 이중 사용을 막습니다. 모든 transact는 입력 2개, 출력 3개의 고정 형태입니다. 실제 입력이 하나뿐이면 팬텀 입력이 빈 자리를 채우고 팬텀 널리파이어를 내며 남는 출력 자리는 amount가 0으로 강제되는 더미 출력이 채웁니다. 관찰자는 팬텀과 실제를 구분할 수 없어야 합니다. 해시는 BN254 스칼라 필드 위 Poseidon2(t=4, R_F=8, R_P=56)의 길이 태그 스펀지 형태로 통일됩니다.

인증 정책 레지스트리와 키 회전

깊이 32의 희소 머클 트리 하나가 모든 사용자의 인증 정책을 담습니다. 리프는 poseidon(AUTH_POLICY_DOMAIN, uint160(user), ownerNullifierKeyHash, noteSecretSeedHash, policySetCommitment)이고 각 주소는 leafPosition 하나를 영구 배정받습니다. 개별 정책은 policyCommitment = poseidon(POLICY_COMMITMENT_DOMAIN, uint160(authVerifier), authDataCommitment, registrationBlinder)로 깊이 POLICY_SET_DEPTH = 8 트리에 숨겨져 등록 시점에는 어느 주소가 어떤 인증 방식을 쓰는지 드러나지 않습니다(authVerifier는 지출 시점에 공개 입력으로 드러납니다). ownerNullifierKeyHash는 회전 불가라 바꾸려면 새 주소로 새 신원을 등록하고 노트를 이전해야 합니다. noteSecretSeedHash와 정책 집합은 setAuthPolicy로 바꿀 수 있지만 이전 상태가 AUTH_POLICY_ROOT_HISTORY_BLOCKS = 64 블록의 루트 히스토리 창이 지날 때까지 유효하므로 회전이 즉시 효력을 갖지 않습니다.

transact 실행 절차

컨트랙트는 19개 공개 입력을 받아 순서대로 검사합니다. 체인 ID 일치, 의도 만료(validUntilSeconds는 block.timestamp + MAX_INTENT_LIFETIME = 86400초 이내), 노트 커밋먼트 루트(현재 루트 또는 크기 NOTE_COMMITMENT_ROOT_HISTORY_SIZE = 500의 히스토리), 인증 정책 루트, 널리파이어 중복, 범위 검사(publicAmountOut < 2^248, 주소류 < 2^160)를 확인한 뒤 풀 증명을 내장 키로, 인증 증명을 authVerifier staticcall로 검증합니다. 이어 널리파이어 2개와 intentReplayId를 소비 처리하고 출력 노트 데이터 3개의 keccak256 mod p 해시를 공개 입력과 대조합니다. publicAmountOut > 0이면 출금으로 ETH 또는 ERC-20을 공개 지급하고 0이면 순수 비공개 전송입니다. 마지막으로 출력 커밋먼트 3개를 트리에 넣고 ShieldedPoolTransact 이벤트를 냅니다.

증명 없는 입금과 ERC-20 호환성 제한

deposit은 증명 없이 동작합니다. ETH는 msg.value == amount를, ERC-20은 transferFrom 전후의 balanceOf 차이가 정확히 amount인지 확인한 뒤 노트 하나를 삽입합니다. 컨트랙트는 ownerCommitment가 등록된 주소에 대응하는지 검증하지 않고 outputNoteData도 해석하지 않습니다. 보내는 쪽이 오프체인에서 수신자의 ownerNullifierKeyHash를 알아내 noteSecret과 함께 ownerCommitment를 계산하는 흐름입니다. 잔액 차이 검사 때문에 전송 수수료형 토큰은 입금이 거부되고 리베이싱 토큰은 신뢰성 있게 감지할 수 없으며 출금(transfer) 쪽에서만 수수료를 떼는 토큰은 입금은 통과해도 출금 때 모자라게 전달되므로 입금하면 안 됩니다. 노트 전달, 멤풀 암호화, 네트워크 익명성, 지갑 통합은 이 EIP 범위 밖으로 남습니다.

진행 상태: Review, Hegotá 포함 제외

이 EIP는 Review 상태라 명세가 아직 바뀔 수 있습니다. 코어 개발자 콜에서는 09.10 ACDE #245에서 Hegotá 포크 후보 중 제외(DFI)로 결정됐습니다. EIP-2488, EIP-8219, EIP-7645와 함께 네 건 모두 전 클라이언트 만장일치 저평가(0.0~0.8)를 받았습니다.

EIP 원문 · ← EIPs