Skip to content

Commit 2890f06

Browse files
pdyragakaralabe
authored andcommitted
core/vm, crypto/blake2b: add BLAKE2b compression func at 0x09
The precompile at 0x09 wraps the BLAKE2b F compression function: https://tools.ietf.org/html/rfc7693#section-3.2 The precompile requires 6 inputs tightly encoded, taking exactly 213 bytes, as explained below. - `rounds` - the number of rounds - 32-bit unsigned big-endian word - `h` - the state vector - 8 unsigned 64-bit little-endian words - `m` - the message block vector - 16 unsigned 64-bit little-endian words - `t_0, t_1` - offset counters - 2 unsigned 64-bit little-endian words - `f` - the final block indicator flag - 8-bit word [4 bytes for rounds][64 bytes for h][128 bytes for m][8 bytes for t_0] [8 bytes for t_1][1 byte for f] The boolean `f` parameter is considered as `true` if set to `1`. The boolean `f` parameter is considered as `false` if set to `0`. All other values yield an invalid encoding of `f` error. The precompile should compute the F function as specified in the RFC (https://tools.ietf.org/html/rfc7693#section-3.2) and return the updated state vector `h` with unchanged encoding (little-endian). See EIP-152 for details.
1 parent dbb03fe commit 2890f06

File tree

5 files changed

+448
-3
lines changed

5 files changed

+448
-3
lines changed

core/vm/contracts.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,14 @@ package vm
1818

1919
import (
2020
"crypto/sha256"
21+
"encoding/binary"
2122
"errors"
2223
"math/big"
2324

2425
"github.com/ethereum/go-ethereum/common"
2526
"github.com/ethereum/go-ethereum/common/math"
2627
"github.com/ethereum/go-ethereum/crypto"
28+
"github.com/ethereum/go-ethereum/crypto/blake2b"
2729
"github.com/ethereum/go-ethereum/crypto/bn256"
2830
"github.com/ethereum/go-ethereum/params"
2931
"golang.org/x/crypto/ripemd160"
@@ -70,6 +72,7 @@ var PrecompiledContractsIstanbul = map[common.Address]PrecompiledContract{
7072
common.BytesToAddress([]byte{6}): &bn256AddIstanbul{},
7173
common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{},
7274
common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{},
75+
common.BytesToAddress([]byte{9}): &blake2F{},
7376
}
7477

7578
// RunPrecompiledContract runs and evaluates the output of a precompiled contract.
@@ -431,3 +434,67 @@ func (c *bn256PairingByzantium) RequiredGas(input []byte) uint64 {
431434
func (c *bn256PairingByzantium) Run(input []byte) ([]byte, error) {
432435
return runBn256Pairing(input)
433436
}
437+
438+
type blake2F struct{}
439+
440+
func (c *blake2F) RequiredGas(input []byte) uint64 {
441+
if len(input) != blake2FInputLength {
442+
// Input is malformed, we can't read the number of rounds.
443+
// Precompile can't be executed so we set its price to 0.
444+
return 0
445+
}
446+
447+
rounds := binary.BigEndian.Uint32(input[0:4])
448+
return uint64(rounds)
449+
}
450+
451+
const blake2FInputLength = 213
452+
const blake2FFinalBlockBytes = byte(1)
453+
const blake2FNonFinalBlockBytes = byte(0)
454+
455+
var errBlake2FIncorrectInputLength = errors.New(
456+
"input length for Blake2 F precompile should be exactly 213 bytes",
457+
)
458+
459+
var errBlake2FIncorrectFinalBlockIndicator = errors.New(
460+
"incorrect final block indicator flag",
461+
)
462+
463+
func (c *blake2F) Run(input []byte) ([]byte, error) {
464+
if len(input) != blake2FInputLength {
465+
return nil, errBlake2FIncorrectInputLength
466+
}
467+
if input[212] != blake2FNonFinalBlockBytes && input[212] != blake2FFinalBlockBytes {
468+
return nil, errBlake2FIncorrectFinalBlockIndicator
469+
}
470+
471+
rounds := binary.BigEndian.Uint32(input[0:4])
472+
473+
var h [8]uint64
474+
for i := 0; i < 8; i++ {
475+
offset := 4 + i*8
476+
h[i] = binary.LittleEndian.Uint64(input[offset : offset+8])
477+
}
478+
479+
var m [16]uint64
480+
for i := 0; i < 16; i++ {
481+
offset := 68 + i*8
482+
m[i] = binary.LittleEndian.Uint64(input[offset : offset+8])
483+
}
484+
485+
var t [2]uint64
486+
t[0] = binary.LittleEndian.Uint64(input[196:204])
487+
t[1] = binary.LittleEndian.Uint64(input[204:212])
488+
489+
f := (input[212] == blake2FFinalBlockBytes)
490+
491+
blake2b.F(&h, m, t, f, rounds)
492+
493+
var output [64]byte
494+
for i := 0; i < 8; i++ {
495+
offset := i * 8
496+
binary.LittleEndian.PutUint64(output[offset:offset+8], h[i])
497+
}
498+
499+
return output[:], nil
500+
}

core/vm/contracts_test.go

Lines changed: 90 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package vm
1919
import (
2020
"fmt"
2121
"math/big"
22+
"reflect"
2223
"testing"
2324

2425
"github.com/ethereum/go-ethereum/common"
@@ -32,6 +33,14 @@ type precompiledTest struct {
3233
noBenchmark bool // Benchmark primarily the worst-cases
3334
}
3435

36+
// precompiledFailureTest defines the input/error pairs for precompiled
37+
// contract failure tests.
38+
type precompiledFailureTest struct {
39+
input string
40+
expectedError error
41+
name string
42+
}
43+
3544
// modexpTests are the test and benchmark data for the modexp precompiled contract.
3645
var modexpTests = []precompiledTest{
3746
{
@@ -336,8 +345,56 @@ var bn256PairingTests = []precompiledTest{
336345
},
337346
}
338347

348+
// EIP-152 test vectors
349+
var blake2FMalformedInputTests = []precompiledFailureTest{
350+
{
351+
input: "",
352+
expectedError: errBlake2FIncorrectInputLength,
353+
name: "vector 0: empty input",
354+
},
355+
{
356+
input: "00000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001",
357+
expectedError: errBlake2FIncorrectInputLength,
358+
name: "vector 1: less than 213 bytes input",
359+
},
360+
{
361+
input: "000000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001",
362+
expectedError: errBlake2FIncorrectInputLength,
363+
name: "vector 2: more than 213 bytes input",
364+
},
365+
{
366+
input: "0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000002",
367+
expectedError: errBlake2FIncorrectFinalBlockIndicator,
368+
name: "vector 3: malformed final block indicator flag",
369+
},
370+
}
371+
372+
// EIP-152 test vectors
373+
var blake2FTests = []precompiledTest{
374+
{
375+
input: "0000000048c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001",
376+
expected: "08c9bcf367e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d282e6ad7f520e511f6c3e2b8c68059b9442be0454267ce079217e1319cde05b",
377+
name: "vector 4",
378+
},
379+
{ // https://tools.ietf.org/html/rfc7693#appendix-A
380+
input: "0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001",
381+
expected: "ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923",
382+
name: "vector 5",
383+
},
384+
{
385+
input: "0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000",
386+
expected: "75ab69d3190a562c51aef8d88f1c2775876944407270c42c9844252c26d2875298743e7f6d5ea2f2d3e8d226039cd31b4e426ac4f2d3d666a610c2116fde4735",
387+
name: "vector 6",
388+
},
389+
{
390+
input: "0000000148c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001",
391+
expected: "b63a380cb2897d521994a85234ee2c181b5f844d2c624c002677e9703449d2fba551b3a8333bcdf5f2f7e08993d53923de3d64fcc68c034e717b9293fed7a421",
392+
name: "vector 7",
393+
},
394+
}
395+
339396
func testPrecompiled(addr string, test precompiledTest, t *testing.T) {
340-
p := PrecompiledContractsByzantium[common.HexToAddress(addr)]
397+
p := PrecompiledContractsIstanbul[common.HexToAddress(addr)]
341398
in := common.Hex2Bytes(test.input)
342399
contract := NewContract(AccountRef(common.HexToAddress("1337")),
343400
nil, new(big.Int), p.RequiredGas(in))
@@ -350,11 +407,25 @@ func testPrecompiled(addr string, test precompiledTest, t *testing.T) {
350407
})
351408
}
352409

410+
func testPrecompiledFailure(addr string, test precompiledFailureTest, t *testing.T) {
411+
p := PrecompiledContractsIstanbul[common.HexToAddress(addr)]
412+
in := common.Hex2Bytes(test.input)
413+
contract := NewContract(AccountRef(common.HexToAddress("31337")),
414+
nil, new(big.Int), p.RequiredGas(in))
415+
416+
t.Run(test.name, func(t *testing.T) {
417+
_, err := RunPrecompiledContract(p, in, contract)
418+
if !reflect.DeepEqual(err, test.expectedError) {
419+
t.Errorf("Expected error [%v], got [%v]", test.expectedError, err)
420+
}
421+
})
422+
}
423+
353424
func benchmarkPrecompiled(addr string, test precompiledTest, bench *testing.B) {
354425
if test.noBenchmark {
355426
return
356427
}
357-
p := PrecompiledContractsByzantium[common.HexToAddress(addr)]
428+
p := PrecompiledContractsIstanbul[common.HexToAddress(addr)]
358429
in := common.Hex2Bytes(test.input)
359430
reqGas := p.RequiredGas(in)
360431
contract := NewContract(AccountRef(common.HexToAddress("1337")),
@@ -481,3 +552,20 @@ func BenchmarkPrecompiledBn256Pairing(bench *testing.B) {
481552
benchmarkPrecompiled("08", test, bench)
482553
}
483554
}
555+
func TestPrecompiledBlake2F(t *testing.T) {
556+
for _, test := range blake2FTests {
557+
testPrecompiled("09", test, t)
558+
}
559+
}
560+
561+
func BenchmarkPrecompiledBlake2F(bench *testing.B) {
562+
for _, test := range blake2FTests {
563+
benchmarkPrecompiled("09", test, bench)
564+
}
565+
}
566+
567+
func TestPrecompileBlake2FMalformedInput(t *testing.T) {
568+
for _, test := range blake2FMalformedInputTests {
569+
testPrecompiledFailure("09", test, t)
570+
}
571+
}

0 commit comments

Comments
 (0)