EIP-152 BLAKE2 압축 함수 F 프리컴파일 (Add BLAKE2 compression function `F` precompile)
최종 확정 · EVM·실행 · 원문 상태 Final · 2016-10-04 작성
BLAKE2 해시 알고리즘의 핵심인 압축 함수 F를 계산하는 프리컴파일(클라이언트에 미리 내장된 계약)을 주소 0x09에 추가합니다. 지캐시 같은 체인과의 상호운용 비용이 크게 낮아집니다.
왜 필요한가
BLAKE2는 SHA3 공모 최종 후보에 올랐던 해시 함수로, 요즘 64비트 CPU에서 MD5보다 빠를 만큼 최적화돼 있습니다. 그런데 이더리움 가상머신(EVM)은 32바이트 빅엔디언 방식으로 계산하도록 설계돼 있어 64비트 리틀엔디언 연산에 맞춰진 BLAKE2를 스마트 계약 코드로 돌리면 실제 계산량에 비해 가스비가 과도하게 비싸진다는 문제가 있습니다. 특히 지캐시(Zcash)가 쓰는 작업증명 Equihash를 한 번 검증하려면 해시 함수를 512번 돌려야 해서 솔리디티로 구현한 BLAKE2로는 지캐시 블록 헤더 검증이 사실상 불가능할 만큼 비쌉니다. 이 함수를 클라이언트가 네이티브 코드로 직접 계산해 주는 프리컴파일로 제공하면, 이더리움 위에서 지캐시 블록을 검증하는 BTC Relay 방식의 SPV 클라이언트나 두 체인 사이의 무신뢰 아토믹 스왑 같은 상호운용이 가능해지고 매우 공개적인 이더리움에 프라이버시 측면을 더할 수 있습니다.
무엇이 바뀌나
지금
- BLAKE2를 쓰려면 솔리디티 같은 계약 코드로 직접 구현해야 하고 실제 계산 비용에 비해 가스비가 과도합니다
- 지캐시 블록 헤더 하나를 검증하려면 해시 512회가 필요해 비용이 감당하기 어렵습니다
- 트리 해싱, 증분 해싱, 키·솔트·개인화 해싱, 가변 길이 다이제스트 같은 기능을 EVM에서 쓸 수 없습니다
EIP-152 이후
- 주소 0x09의 프리컴파일이 BLAKE2 압축 함수 F를 네이티브 속도로 계산해 줍니다
- 비용이 라운드당 1가스로 책정돼 BLAKE2b 등 64비트 BLAKE2 변형을 싸게 쓸 수 있습니다
- 지캐시와의 무신뢰 아토믹 스왑, SPV 클라이언트 같은 상호운용 계약을 만들 수 있는 기반이 생깁니다
누구에게 영향이 있나
- 계약·앱 개발자: F 함수를 솔리디티로 감싸서 트리 해싱, 증분 해싱, 키·솔트·개인화 해싱, 가변 길이 다이제스트를 직접 구현할 수 있습니다
- 상호운용 프로젝트: 지캐시 릴레이에 꼭 필요한 최소 요건이 충족되고 원문에 따르면 Filecoin과 Handshake의 상호운용 목표에도 충분합니다
- 클라이언트 팀: 주소 0x09에 F 압축 함수 프리컴파일을 구현해야 합니다. go-ethereum 포크의 참조 구현이 원문에 링크돼 있습니다
- 사용자: 체인 간 무신뢰 아토믹 스왑이 가능해지면 공개적인 이더리움 거래에 프라이버시 선택지가 생길 수 있습니다
심층 설명
주소 0x09와 213바이트 입력 인코딩
프리컴파일은 주소 0x09에 놓이며 RFC 7693 3.2절의 BLAKE2 압축 함수 F를 감쌉니다. 입력은 6개 값을 빈틈없이 이어 붙인 정확히 213바이트입니다. rounds는 라운드 수로 32비트 부호 없는 빅엔디언 워드, h는 상태 벡터로 부호 없는 64비트 리틀엔디언 워드 8개, m은 메시지 블록 벡터로 부호 없는 64비트 리틀엔디언 워드 16개, t_0과 t_1은 오프셋 카운터로 부호 없는 64비트 리틀엔디언 워드 2개, f는 최종 블록 표시 플래그로 8비트 워드입니다. 배치는 [rounds 4바이트][h 64바이트][m 128바이트][t_0 8바이트][t_1 8바이트][f 1바이트] 순서입니다. 출력은 갱신된 h를 리틀엔디언 인코딩 그대로 반환합니다. rounds만 빅엔디언이고 나머지 워드는 리틀엔디언이라는 점이 특징입니다.
엄격한 입력 검증과 에러 조건
입력 길이가 213바이트에서 1바이트라도 모자라거나 넘치면 "input length for BLAKE2 F precompile should be exactly 213 bytes" 에러가 납니다. 테스트 벡터 0(빈 입력), 1(213바이트 미달), 2(214바이트 초과)가 이를 확인합니다. f 플래그는 1이면 참, 0이면 거짓이고 다른 모든 값은 잘못된 인코딩입니다. 테스트 벡터 3은 f에 2를 넣으면 "incorrect final block indicator flag" 에러가 나는 것을 보여 줍니다. 테스트 벡터 5는 12라운드에 메시지 "abc"를 넣는 알려진 답 테스트로, 출력이 ba80a53f...로 시작하는 BLAKE2b의 표준 결과와 일치해야 합니다. rounds가 0인 경우(벡터 4), f가 0인 경우(벡터 6), 1라운드(벡터 7), 최대값 0xffffffff 라운드(벡터 8)도 기대 출력이 정의돼 있습니다.
가스 비용: GFROUND * rounds, GFROUND = 1
호출 한 번의 비용은 GFROUND * rounds이고 GFROUND = 1입니다. 즉 라운드당 1가스입니다. 가격 책정 근거는 ecRecover 프리컴파일이 완벽하게 책정됐다고 가정하고 3.1GHz Intel Core i7에서 돌린 벤치마크입니다. 12라운드 호출은 ecRecover와 사이클당 비용을 맞추려면 평균 6.74153가스(라운드당 0.5618가스)가 적정했고 1200라운드는 436.1288가스(라운드당 0.3634가스)였습니다. 1라운드는 2.431701가스가 적정치로 나왔지만 이 경우 호출 비용 자체가 동적 비용을 압도합니다. 실제 채택된 라운드당 1가스는 이 측정값들보다 다소 높은 쪽입니다.
왜 전체 해시 함수가 아니라 압축 함수 F만 넣었는가
언뜻 보면 BLAKE2b 해시 함수 전체를 프리컴파일로 넣는 것이 자연스럽습니다. 하지만 프로젝트마다 요구하는 기능과 내부 수정이 달라서 어떤 BLAKE2b 구현이든 개인화(personalization) 같은 특정 API를 지원해야 하는 문제가 드러났습니다. 지캐시 팀과의 논의에서 ZEC-ETH 릴레이에 꼭 필요한 최소 요건은 압축 함수 F 프리컴파일이며 이것으로 Filecoin과 Handshake의 상호운용 목표에도 충분하다는 결론이 나왔습니다. 전체 BLAKE2b 프리컴파일은 있으면 좋지만 완전한 요구사항과 유연한 API를 이스탄불(Istanbul) 일정 안에 확정할 수 있을지 불분명했습니다. F만 구현하면 프로토콜 변경을 최소화하면서 트리 해싱, 증분 해싱, 키·솔트·개인화 해싱, 가변 길이 다이제스트처럼 EVM에 없던 기능을 그 위에 만들 수 있습니다.
EVM에서 BLAKE2가 구조적으로 비싼 이유
BLAKE2는 현대 64비트 CPU에 맞춰 강하게 최적화돼 있습니다. 24비트와 63비트 회전 연산을 써서 SIMD 명령어로 병렬화할 수 있고 리틀엔디언 산술을 사용합니다. 그 결과 네이티브 CPU에서 바이트당 3.08사이클, Intel i5 기준 초당 1기비바이트라는 속도가 나옵니다. 반면 EVM은 32바이트 빅엔디언 의미론을 쓰기 때문에 BLAKE2를 효율적으로 구현하기 어렵고 EVM 위에서 이 해시를 계산하는 가스 비용이 네이티브로 계산하는 실제 비용과 심하게 어긋납니다. 프리컴파일은 계산을 클라이언트의 네이티브 코드로 옮겨 이 격차를 없앱니다.
하위 호환성과 구현
이 EIP가 기존 동작을 깨뜨릴 위험은 매우 작습니다. 주소 0x09가 비어 있다는 데 의존하는 계약을 누군가 만들어 둔 경우가 유일한 문제인데, 그럴 가능성은 낮고 구체적인 사례가 나타나면 충돌 위험이 무시할 만한 임의의 다른 주소를 고를 수도 있다고 원문은 설명합니다. 참조 구현으로는 Go 표준 라이브러리를 변형한 F 함수 구현(keep-network의 blake2-f 포크)과 go-ethereum 포크의 프리컴파일 구현이 링크돼 있습니다. 이 EIP의 상태는 Final이며 본문에서 전체 BLAKE2b 프리컴파일 논의 당시 목표 포크로 이스탄불을 언급합니다.