// Copyright 2017 The go-ethereum Authors // This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // The go-ethereum library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . package vm import ( "bytes" "encoding/json" "fmt" "io/ioutil" "math/big" "testing" "github.com/ethereum/go-ethereum/common" ) // precompiledTest defines the input/output pairs for precompiled contract tests. type precompiledTest struct { Input, Expected string Name string NoBenchmark bool // Benchmark primarily the worst-cases } // precompiledFailureTest defines the input/error pairs for precompiled // contract failure tests. type precompiledFailureTest struct { Input string ExpectedError string Name string } var allPrecompiles = PrecompiledContractsYoloV1 // EIP-152 test vectors var blake2FMalformedInputTests = []precompiledFailureTest{ { Input: "", ExpectedError: errBlake2FInvalidInputLength.Error(), Name: "vector 0: empty input", }, { Input: "00000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001", ExpectedError: errBlake2FInvalidInputLength.Error(), Name: "vector 1: less than 213 bytes input", }, { Input: "000000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001", ExpectedError: errBlake2FInvalidInputLength.Error(), Name: "vector 2: more than 213 bytes input", }, { Input: "0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000002", ExpectedError: errBlake2FInvalidFinalFlag.Error(), Name: "vector 3: malformed final block indicator flag", }, } func testPrecompiled(addr string, test precompiledTest, t *testing.T) { p := allPrecompiles[common.HexToAddress(addr)] in := common.Hex2Bytes(test.Input) contract := NewContract(AccountRef(common.HexToAddress("1337")), nil, new(big.Int), p.RequiredGas(in)) t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, contract.Gas), func(t *testing.T) { if res, err := RunPrecompiledContract(p, in, contract); err != nil { t.Error(err) } else if common.Bytes2Hex(res) != test.Expected { t.Errorf("Expected %v, got %v", test.Expected, common.Bytes2Hex(res)) } // Verify that the precompile did not touch the input buffer exp := common.Hex2Bytes(test.Input) if !bytes.Equal(in, exp) { t.Errorf("Precompiled %v modified input data", addr) } }) } func testPrecompiledOOG(addr string, test precompiledTest, t *testing.T) { p := allPrecompiles[common.HexToAddress(addr)] in := common.Hex2Bytes(test.Input) contract := NewContract(AccountRef(common.HexToAddress("1337")), nil, new(big.Int), p.RequiredGas(in)-1) t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, contract.Gas), func(t *testing.T) { _, err := RunPrecompiledContract(p, in, contract) if err.Error() != "out of gas" { t.Errorf("Expected error [out of gas], got [%v]", err) } // Verify that the precompile did not touch the input buffer exp := common.Hex2Bytes(test.Input) if !bytes.Equal(in, exp) { t.Errorf("Precompiled %v modified input data", addr) } }) } func testPrecompiledFailure(addr string, test precompiledFailureTest, t *testing.T) { p := allPrecompiles[common.HexToAddress(addr)] in := common.Hex2Bytes(test.Input) contract := NewContract(AccountRef(common.HexToAddress("31337")), nil, new(big.Int), p.RequiredGas(in)) t.Run(test.Name, func(t *testing.T) { _, err := RunPrecompiledContract(p, in, contract) if err.Error() != test.ExpectedError { t.Errorf("Expected error [%v], got [%v]", test.ExpectedError, err) } // Verify that the precompile did not touch the input buffer exp := common.Hex2Bytes(test.Input) if !bytes.Equal(in, exp) { t.Errorf("Precompiled %v modified input data", addr) } }) } func benchmarkPrecompiled(addr string, test precompiledTest, bench *testing.B) { if test.NoBenchmark { return } p := allPrecompiles[common.HexToAddress(addr)] in := common.Hex2Bytes(test.Input) reqGas := p.RequiredGas(in) contract := NewContract(AccountRef(common.HexToAddress("1337")), nil, new(big.Int), reqGas) var ( res []byte err error data = make([]byte, len(in)) ) bench.Run(fmt.Sprintf("%s-Gas=%d", test.Name, contract.Gas), func(bench *testing.B) { bench.ReportAllocs() bench.ResetTimer() for i := 0; i < bench.N; i++ { contract.Gas = reqGas copy(data, in) res, err = RunPrecompiledContract(p, data, contract) } bench.StopTimer() bench.ReportMetric(float64(reqGas), "gas/op") //Check if it is correct if err != nil { bench.Error(err) return } if common.Bytes2Hex(res) != test.Expected { bench.Error(fmt.Sprintf("Expected %v, got %v", test.Expected, common.Bytes2Hex(res))) return } }) } // Benchmarks the sample inputs from the ECRECOVER precompile. func BenchmarkPrecompiledEcrecover(bench *testing.B) { t := precompiledTest{ Input: "38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e000000000000000000000000000000000000000000000000000000000000001b38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e789d1dd423d25f0772d2748d60f7e4b81bb14d086eba8e8e8efb6dcff8a4ae02", Expected: "000000000000000000000000ceaccac640adf55b2028469bd36ba501f28b699d", Name: "", } benchmarkPrecompiled("01", t, bench) } // Benchmarks the sample inputs from the SHA256 precompile. func BenchmarkPrecompiledSha256(bench *testing.B) { t := precompiledTest{ Input: "38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e000000000000000000000000000000000000000000000000000000000000001b38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e789d1dd423d25f0772d2748d60f7e4b81bb14d086eba8e8e8efb6dcff8a4ae02", Expected: "811c7003375852fabd0d362e40e68607a12bdabae61a7d068fe5fdd1dbbf2a5d", Name: "128", } benchmarkPrecompiled("02", t, bench) } // Benchmarks the sample inputs from the RIPEMD precompile. func BenchmarkPrecompiledRipeMD(bench *testing.B) { t := precompiledTest{ Input: "38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e000000000000000000000000000000000000000000000000000000000000001b38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e789d1dd423d25f0772d2748d60f7e4b81bb14d086eba8e8e8efb6dcff8a4ae02", Expected: "0000000000000000000000009215b8d9882ff46f0dfde6684d78e831467f65e6", Name: "128", } benchmarkPrecompiled("03", t, bench) } // Benchmarks the sample inputs from the identiy precompile. func BenchmarkPrecompiledIdentity(bench *testing.B) { t := precompiledTest{ Input: "38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e000000000000000000000000000000000000000000000000000000000000001b38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e789d1dd423d25f0772d2748d60f7e4b81bb14d086eba8e8e8efb6dcff8a4ae02", Expected: "38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e000000000000000000000000000000000000000000000000000000000000001b38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e789d1dd423d25f0772d2748d60f7e4b81bb14d086eba8e8e8efb6dcff8a4ae02", Name: "128", } benchmarkPrecompiled("04", t, bench) } // Tests the sample inputs from the ModExp EIP 198. func TestPrecompiledModExp(t *testing.T) { testJson("modexp", "05", t) } func BenchmarkPrecompiledModExp(b *testing.B) { benchJson("modexp", "05", b) } // Tests the sample inputs from the elliptic curve addition EIP 213. func TestPrecompiledBn256Add(t *testing.T) { testJson("bn256Add", "06", t) } func BenchmarkPrecompiledBn256Add(b *testing.B) { benchJson("bn256Add", "06", b) } // Tests OOG func TestPrecompiledModExpOOG(t *testing.T) { modexpTests, err := loadJson("modexp") if err != nil { t.Fatal(err) } for _, test := range modexpTests { testPrecompiledOOG("05", test, t) } } // Tests the sample inputs from the elliptic curve scalar multiplication EIP 213. func TestPrecompiledBn256ScalarMul(t *testing.T) { testJson("bn256ScalarMul", "07", t) } func BenchmarkPrecompiledBn256ScalarMul(b *testing.B) { benchJson("bn256ScalarMul", "07", b) } // Tests the sample inputs from the elliptic curve pairing check EIP 197. func TestPrecompiledBn256Pairing(t *testing.T) { testJson("bn256Pairing", "08", t) } func BenchmarkPrecompiledBn256Pairing(b *testing.B) { benchJson("bn256Pairing", "08", b) } func TestPrecompiledBlake2F(t *testing.T) { testJson("blake2F", "09", t) } func BenchmarkPrecompiledBlake2F(b *testing.B) { benchJson("blake2F", "09", b) } func TestPrecompileBlake2FMalformedInput(t *testing.T) { for _, test := range blake2FMalformedInputTests { testPrecompiledFailure("09", test, t) } } func TestPrecompiledEcrecover(t *testing.T) { testJson("ecRecover", "01", t) } func testJson(name, addr string, t *testing.T) { tests, err := loadJson(name) if err != nil { t.Fatal(err) } for _, test := range tests { testPrecompiled(addr, test, t) } } func testJsonFail(name, addr string, t *testing.T) { tests, err := loadJsonFail(name) if err != nil { t.Fatal(err) } for _, test := range tests { testPrecompiledFailure(addr, test, t) } } func benchJson(name, addr string, b *testing.B) { tests, err := loadJson(name) if err != nil { b.Fatal(err) } for _, test := range tests { benchmarkPrecompiled(addr, test, b) } } func TestPrecompiledBLS12381G1Add(t *testing.T) { testJson("blsG1Add", "0a", t) } func TestPrecompiledBLS12381G1Mul(t *testing.T) { testJson("blsG1Mul", "0b", t) } func TestPrecompiledBLS12381G1MultiExp(t *testing.T) { testJson("blsG1MultiExp", "0c", t) } func TestPrecompiledBLS12381G2Add(t *testing.T) { testJson("blsG2Add", "0d", t) } func TestPrecompiledBLS12381G2Mul(t *testing.T) { testJson("blsG2Mul", "0e", t) } func TestPrecompiledBLS12381G2MultiExp(t *testing.T) { testJson("blsG2MultiExp", "0f", t) } func TestPrecompiledBLS12381Pairing(t *testing.T) { testJson("blsPairing", "10", t) } func TestPrecompiledBLS12381MapG1(t *testing.T) { testJson("blsMapG1", "11", t) } func TestPrecompiledBLS12381MapG2(t *testing.T) { testJson("blsMapG2", "12", t) } func BenchmarkPrecompiledBLS12381G1Add(b *testing.B) { benchJson("blsG1Add", "0a", b) } func BenchmarkPrecompiledBLS12381G1Mul(b *testing.B) { benchJson("blsG1Mul", "0b", b) } func BenchmarkPrecompiledBLS12381G1MultiExp(b *testing.B) { benchJson("blsG1MultiExp", "0c", b) } func BenchmarkPrecompiledBLS12381G2Add(b *testing.B) { benchJson("blsG2Add", "0d", b) } func BenchmarkPrecompiledBLS12381G2Mul(b *testing.B) { benchJson("blsG2Mul", "0e", b) } func BenchmarkPrecompiledBLS12381G2MultiExp(b *testing.B) { benchJson("blsG2MultiExp", "0f", b) } func BenchmarkPrecompiledBLS12381Pairing(b *testing.B) { benchJson("blsPairing", "10", b) } func BenchmarkPrecompiledBLS12381MapG1(b *testing.B) { benchJson("blsMapG1", "11", b) } func BenchmarkPrecompiledBLS12381MapG2(b *testing.B) { benchJson("blsMapG2", "12", b) } // Failure tests func TestPrecompiledBLS12381G1AddFail(t *testing.T) { testJsonFail("blsG1Add", "0a", t) } func TestPrecompiledBLS12381G1MulFail(t *testing.T) { testJsonFail("blsG1Mul", "0b", t) } func TestPrecompiledBLS12381G1MultiExpFail(t *testing.T) { testJsonFail("blsG1MultiExp", "0c", t) } func TestPrecompiledBLS12381G2AddFail(t *testing.T) { testJsonFail("blsG2Add", "0d", t) } func TestPrecompiledBLS12381G2MulFail(t *testing.T) { testJsonFail("blsG2Mul", "0e", t) } func TestPrecompiledBLS12381G2MultiExpFail(t *testing.T) { testJsonFail("blsG2MultiExp", "0f", t) } func TestPrecompiledBLS12381PairingFail(t *testing.T) { testJsonFail("blsPairing", "10", t) } func TestPrecompiledBLS12381MapG1Fail(t *testing.T) { testJsonFail("blsMapG1", "11", t) } func TestPrecompiledBLS12381MapG2Fail(t *testing.T) { testJsonFail("blsMapG2", "12", t) } func loadJson(name string) ([]precompiledTest, error) { data, err := ioutil.ReadFile(fmt.Sprintf("testdata/precompiles/%v.json", name)) if err != nil { return nil, err } var testcases []precompiledTest err = json.Unmarshal(data, &testcases) return testcases, err } func loadJsonFail(name string) ([]precompiledFailureTest, error) { data, err := ioutil.ReadFile(fmt.Sprintf("testdata/precompiles/fail-%v.json", name)) if err != nil { return nil, err } var testcases []precompiledFailureTest err = json.Unmarshal(data, &testcases) return testcases, err }