Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Blake2b F precompile #2

Closed
wants to merge 9 commits into from

Conversation

mhluongo
Copy link
Member

Adapt ethereum#152 into the proper format.

Confirming a couple details before I open this against upstream. cc @pdyraga

mhluongo added 5 commits June 16, 2019 00:11
Also added myself as an author
Should have a working geth precompile and initial benchmarks shortly
While 2046's cheaper precompile contract calls aren't a requirement for
this EIP's implementation, shipping this precompile without EIP-2046
would make the F function expensive for some of the motivating usecases.
## Implementation
<!--The implementations must be completed before any EIP is given status "Final", but it need not be completed before the EIP is accepted. While there is merit to the approach of reaching consensus on the specification and rationale before writing code, the principle of "rough consensus and running code" is still useful when it comes to resolving many discussions of API details.-->

Implementations are in progress, and can be followed along in our [Golang Blake2 library fork](https://github.com/keep-network/blake2-f) as well as our fork of [go-ethereum](https://github.com/keep-network/go-ethereum).
Copy link
Member

@pdyraga pdyraga Jun 17, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is only F function implementation extracted from Golang Blake2 lib, should we make it clear here?
(Original Golang Blake2 lib does not export F out)

## Specification
<!--The technical specification should describe the syntax and semantics of any new feature. The specification should be detailed enough to allow competing, interoperable implementations for any of the current Ethereum platforms (go-ethereum, parity, cpp-ethereum, ethereumj, ethereumjs, and [others](https://github.com/ethereum/wiki/wiki/Clients)).-->

Adds a precompile at address `0x0d` which accepts [ABI encoded](https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI) arguments corresponding to the function signature
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per our discussion, we should update the part about ABI encoded parameters.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I recommend we move implementation discussion to the blake2-f library's first PR- WDYT?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@carterpy Can you please repost your questions in the PR referenced by @mhluongo? I can happily answer them there.

Copy link

@PlainsWraith PlainsWraith Jun 19, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pdyraga, questions removed because they were actually silly :$

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here is an alternative proposition about how to call the F function.

Instead of ABI-encoded parameters (no precompile currently do that) we can use a standard staticcall.

I'd propose the following structure:

[ bytes32[0] | bytes32[1] ][ bytes32[2] | bytes32[3] | bytes32[4] | bytes32[5] ]
[      64 bytes for h     ][                    128 bytes for m                ]

[             bytes32[6]             ][             bytes32[7]             ]
[ 24 bytes padding | 8 bytes for t_0 ][ 24 bytes padding | 8 bytes for t_1 ]

[             bytes32[8]          ][              bytes32[9]              ]
[ 31 bytes padding | 1 byte for f ][ 28 bytes padding | 4 bytes for rounds]

where:

  • h - state vector
  • m - message block vector
  • t_0, t_1 - offset counter
  • f - final block indicator flag
  • rounds - number of rounds

This way we pack data in equal 32-byte (256-bit) slots, we are compatible with the rest of precompile calls and we do not require any bitwise juggling from callers.

For convenience, one may expose a wrapping function, similar to F:

    function F(bytes32[2] memory h, bytes32[4] memory m, uint64[2] memory t, bool f, uint32 rounds) public view returns (bytes32[2] memory) {
      bytes32[2] memory output;

      bytes32 _t0 = bytes32(uint256(t[0]));
      bytes32 _t1 = bytes32(uint256(t[1]));

      bytes32 _f;
      if (f) {
        _f = hex"0000000000000000000000000000000000000000000000000000000000000001";
      }

      bytes32 _rounds = bytes32(uint256(rounds));

      bytes32[10] memory args = [ h[0], h[1], m[0], m[1], m[2], m[3], _t0, _t1, _f, _rounds ];

      assembly {
            if iszero(staticcall(not(0), 0x09, args, 0x140, output, 0x40)) {
                revert(0, 0)
            }
        }
        return output;
    }

    function callF() public view returns (bytes32[2] memory) {
      bytes32[2] memory h;
      h[0] = hex"6a09e627f3bcc909bb67ae8484caa73b3c6ef372fe94b82ba54ff53a5f1d36f1";
      h[1] = hex"510e527fade682d19b05688c2b3e6c1f1f83d9abfb41bd6b5be0cd19137e2179";

      bytes32[4] memory m;
      m[0] = hex"278400340e6b05c5752592c52c4f121292eafc51cc01a997b4aed13409298fad";
      m[1] = hex"0d99ccc8f76453d9b3661e5c4d325d7751147db17489046d1682d50eefa4a1da";
      m[2] = hex"0000000000000000000000000000000000000000000000000000000000000000";
      m[3] = hex"0000000000000000000000000000000000000000000000000000000000000000";

      uint64[2] memory t;
      t[0] = 18446744073709551552;
      t[1] = 18446744073709551615;

      bool f = true;

      uint32 rounds = 12;

      return F(h, m, t, f, rounds);
    }

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(this looks terrible at the first sight, but this is similar to how other precompiles are called with assembly 🤷‍♀ - they are all equally terrible).


Interoperability with Zcash could enable contracts like trustless atomic swaps between the chains, which could provide a much needed aspect of privacy to the very public Ethereum blockchain.

## Specification
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Discussing gas price based on benchmarks from the implementation and using the same approach as we did for EIP-1108...

Let's assume ecrecover is perfectly priced. It costs 3000 gas and takes 180.3 microseconds on my Mac to execute. It's about 16.66 gas per microsecond of ecrecover work.

With this in mind, F took 402 nanoseconds on average, it is equal to 0.402 microseconds so the invocation (assuming the same input parameters) should cost 0.402 * 16.66 = 6.7 gas.

Those benchmarks were done with 12 rounds, so it's 0.56 per round.

In the original EIP there was mentioned a price of 37 gas per round.

We have it either heavily optimized or I am too tired to see where is the mistake in my calculations...

Replace the existing ABI encoding interface to the BLAKE2b `F`
precompile with a loosely pack struct that's `staticcall`-friendly.

H/t to @pdyraga for putting together the interface!
@mhluongo mhluongo force-pushed the blake2b-f-precompile branch from 2c59c17 to 764ff73 Compare June 20, 2019 19:37

The BLAKE2b algorithm is highly optimized for 64-bit CPUs, and is faster than MD5 on modern processors.

Interoperability with Zcash could enable contracts like trustless atomic swaps between the chains, which could provide a much needed aspect of privacy to the very public Ethereum blockchain.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might also mention Sia and Handshake?

Let's not relitigate precompiles, WASM, etc in thie EIP :)
## Specification
<!--The technical specification should describe the syntax and semantics of any new feature. The specification should be detailed enough to allow competing, interoperable implementations for any of the current Ethereum platforms (go-ethereum, parity, cpp-ethereum, ethereumj, ethereumjs, and [others](https://github.com/ethereum/wiki/wiki/Clients)).-->

We propose adding a precompiled contract at address `0x09` wrapping the [BLAkE2b `F` compression function](https://tools.ietf.org/html/rfc7693#section-3.2). Rather than the ABI-encoded call structure proposed in [#152](https://github.com/ethereum/EIPs/issues/152), this design better matches other precompiles and works well with a standard `staticcall`.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

example of other

EIPS/eip-draft-blake2b-f-precompile.md Outdated Show resolved Hide resolved
EIPS/eip-draft-blake2b-f-precompile.md Outdated Show resolved Hide resolved
If a conflicting EIP is moving forward the EIP editor can assign a new
address
@mhluongo
Copy link
Member Author

@prestwich thanks fore the comments- will address in a more public EIP

@mhluongo mhluongo marked this pull request as ready for review June 20, 2019 19:48
@mhluongo
Copy link
Member Author

Closing in favor of an upstream PR ethereum#2129

@mhluongo mhluongo closed this Jun 20, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants