EIP-152 BLAKE2 Compression Function F Precompile (Add BLAKE2 compression function `F` precompile)
Final · EVM & execution · EIP status Final · created 2016-10-04
Adds a precompile (a contract built into the client in advance) at address 0x09 that computes the compression function F at the core of the BLAKE2 hash algorithm. This sharply lowers the cost of interoperating with chains like Zcash.
Why it is needed
BLAKE2 was a finalist in the SHA3 competition and is optimized enough to run faster than MD5 on modern 64-bit CPUs. The problem is that the Ethereum Virtual Machine (EVM) is designed around 32-byte big-endian arithmetic, so running BLAKE2, which is tuned for 64-bit little-endian operations, as smart contract code makes gas costs disproportionately expensive relative to the actual amount of computation. In particular, verifying Zcash's proof-of-work algorithm Equihash once requires running the hash function 512 times, which makes verifying a Zcash block header with a Solidity implementation of BLAKE2 practically prohibitively expensive. Providing this function as a precompile that the client computes directly in native code enables interoperability such as BTC Relay style SPV clients that verify Zcash blocks on Ethereum, or trustless atomic swaps between the two chains, and can add a privacy dimension to an otherwise highly transparent Ethereum.
What changes
Today
- Using BLAKE2 requires implementing it directly in contract code such as Solidity, and the gas cost is disproportionate to the actual computation cost
- Verifying a single Zcash block header requires 512 hash rounds, making the cost unaffordable
- Features like tree hashing, incremental hashing, keyed/salted/personalized hashing, and variable-length digests are unavailable in the EVM
After EIP-152
- The precompile at address 0x09 computes the BLAKE2 compression function F at native speed
- The cost is set at 1 gas per round, making 64-bit BLAKE2 variants such as BLAKE2b cheap to use
- This creates the foundation for interoperability contracts such as trustless atomic swaps and SPV clients with Zcash
Who is affected
- Contract and app developers: They can wrap the F function in Solidity to directly implement tree hashing, incremental hashing, keyed/salted/personalized hashing, and variable-length digests
- Interoperability projects: The minimum requirement for a Zcash relay is satisfied, and according to the original text this is also sufficient for the interoperability goals of Filecoin and Handshake
- Client teams: The F compression function precompile must be implemented at address 0x09. A reference implementation from a go-ethereum fork is linked in the original text
- Users: If trustless atomic swaps between chains become possible, this could create privacy options for otherwise transparent Ethereum transactions
In depth
Address 0x09 and the 213-byte input encoding
The precompile sits at address 0x09 and wraps the BLAKE2 compression function F from RFC 7693 section 3.2. The input is exactly 213 bytes formed by concatenating six values with no gaps. rounds is the number of rounds as a 32-bit unsigned big-endian word, h is the state vector of 8 unsigned 64-bit little-endian words, m is the message block vector of 16 unsigned 64-bit little-endian words, t_0 and t_1 are the offset counters of 2 unsigned 64-bit little-endian words, and f is the final block indicator flag as an 8-bit word. The layout is [rounds 4 bytes][h 64 bytes][m 128 bytes][t_0 8 bytes][t_1 8 bytes][f 1 byte]. The output returns the updated h exactly as little-endian encoding. A distinctive feature is that only rounds is big-endian while the rest of the words are little-endian.
Strict input validation and error conditions
If the input length is even one byte short of or over 213 bytes, it errors with "input length for BLAKE2 F precompile should be exactly 213 bytes". Test vectors 0 (empty input), 1 (under 213 bytes), and 2 (over 214 bytes) confirm this. The f flag is true if 1, false if 0, and any other value is an invalid encoding. Test vector 3 shows that setting f to 2 produces an "incorrect final block indicator flag" error. Test vector 5 is a known-answer test with 12 rounds and the message "abc", where the output must match the standard BLAKE2b result starting with ba80a53f... Expected outputs are also defined for rounds equal to 0 (vector 4), f equal to 0 (vector 6), 1 round (vector 7), and the maximum value of 0xffffffff rounds (vector 8).
Gas cost: GFROUND * rounds, GFROUND = 1
The cost of a single call is GFROUND * rounds, where GFROUND = 1, i.e. 1 gas per round. The pricing rationale assumes the ecRecover precompile is perfectly priced and is based on benchmarks run on a 3.1GHz Intel Core i7. To match cost per cycle with ecRecover, a 12-round call would have an appropriate average of 6.74153 gas (0.5618 gas per round), and 1200 rounds would be 436.1288 gas (0.3634 gas per round). For 1 round, the appropriate figure came out to 2.431701 gas, but in that case the call overhead itself dominates the dynamic cost. The actually adopted rate of 1 gas per round is somewhat higher than these measured values.
Why only the compression function F was included rather than the full hash function
At first glance it seems natural to add the entire BLAKE2b hash function as a precompile. However, since different projects require different features and internal modifications, it became clear that any BLAKE2b implementation would need to support specific APIs such as personalization. Discussions with the Zcash team concluded that the minimum requirement for a ZEC-ETH relay is a compression function F precompile, and that this is also sufficient for the interoperability goals of Filecoin and Handshake. A full BLAKE2b precompile would be nice to have, but it was unclear whether the complete requirements and a flexible API could be finalized within the Istanbul timeline. Implementing only F minimizes protocol changes while allowing features missing from the EVM, such as tree hashing, incremental hashing, keyed/salted/personalized hashing, and variable-length digests, to be built on top of it.
Why BLAKE2 is structurally expensive on the EVM
BLAKE2 is heavily optimized for modern 64-bit CPUs. It uses 24-bit and 63-bit rotation operations that can be parallelized with SIMD instructions, and it uses little-endian arithmetic. As a result, it achieves 3.08 cycles per byte on native CPUs, or 1 gibibyte per second on an Intel i5. The EVM, by contrast, uses 32-byte big-endian semantics, which makes BLAKE2 hard to implement efficiently, and the gas cost of computing this hash on the EVM diverges sharply from the actual cost of computing it natively. The precompile moves the computation into the client's native code, eliminating this gap.
Backwards compatibility and implementation
The risk of this EIP breaking existing behavior is very small. The only concern would be a contract someone built that relies on address 0x09 being empty, which is unlikely, and the original text notes that if a concrete case appears, an arbitrary different address with negligible collision risk could be chosen instead. The reference implementations linked are a modified version of the F function from the Go standard library (a fork of keep-network's blake2-f) and a precompile implementation from a go-ethereum fork. The status of this EIP is Final, and the body mentions Istanbul as the target fork at the time of the full BLAKE2b precompile discussion.