core/vm: fix incorrect computation of BLS discount (#21253)

* core/vm: fix incorrect computation of discount

During testing on Yolov1 we found that the way geth calculates the discount
is not in line with the specification. Basically what we did is calculate
128 * Bls12381GXMulGas * discount / 1000 whenever we received more than 128 pairs
of values. Correct would be to calculate k * Bls12381... for k > 128.

* core/vm: better logic for discount calculation

* core/vm: better calculation logic, added worstcase benchmarks

* core/vm: better benchmarking logic
This commit is contained in:
Marius van der Wijden 2020-06-24 19:58:28 +00:00 committed by GitHub
parent b482423e61
commit 0c82928981
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 57 additions and 8 deletions

View File

@ -620,11 +620,12 @@ func (c *bls12381G1MultiExp) RequiredGas(input []byte) uint64 {
return 0 return 0
} }
// Lookup discount value for G1 point, scalar value pair length // Lookup discount value for G1 point, scalar value pair length
maxDiscountLen := len(params.Bls12381MultiExpDiscountTable) var discount uint64
if k > maxDiscountLen { if dLen := len(params.Bls12381MultiExpDiscountTable); k < dLen {
k = maxDiscountLen discount = params.Bls12381MultiExpDiscountTable[k-1]
} else {
discount = params.Bls12381MultiExpDiscountTable[dLen-1]
} }
discount := params.Bls12381MultiExpDiscountTable[k-1]
// Calculate gas and return the result // Calculate gas and return the result
return (uint64(k) * params.Bls12381G1MulGas * discount) / 1000 return (uint64(k) * params.Bls12381G1MulGas * discount) / 1000
} }
@ -750,11 +751,12 @@ func (c *bls12381G2MultiExp) RequiredGas(input []byte) uint64 {
return 0 return 0
} }
// Lookup discount value for G2 point, scalar value pair length // Lookup discount value for G2 point, scalar value pair length
maxDiscountLen := len(params.Bls12381MultiExpDiscountTable) var discount uint64
if k > maxDiscountLen { if dLen := len(params.Bls12381MultiExpDiscountTable); k < dLen {
k = maxDiscountLen discount = params.Bls12381MultiExpDiscountTable[k-1]
} else {
discount = params.Bls12381MultiExpDiscountTable[dLen-1]
} }
discount := params.Bls12381MultiExpDiscountTable[k-1]
// Calculate gas and return the result // Calculate gas and return the result
return (uint64(k) * params.Bls12381G2MulGas * discount) / 1000 return (uint64(k) * params.Bls12381G2MulGas * discount) / 1000
} }

View File

@ -23,6 +23,7 @@ import (
"io/ioutil" "io/ioutil"
"math/big" "math/big"
"testing" "testing"
"time"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
) )
@ -142,6 +143,7 @@ func benchmarkPrecompiled(addr string, test precompiledTest, bench *testing.B) {
bench.Run(fmt.Sprintf("%s-Gas=%d", test.Name, contract.Gas), func(bench *testing.B) { bench.Run(fmt.Sprintf("%s-Gas=%d", test.Name, contract.Gas), func(bench *testing.B) {
bench.ReportAllocs() bench.ReportAllocs()
start := time.Now().Nanosecond()
bench.ResetTimer() bench.ResetTimer()
for i := 0; i < bench.N; i++ { for i := 0; i < bench.N; i++ {
contract.Gas = reqGas contract.Gas = reqGas
@ -149,7 +151,13 @@ func benchmarkPrecompiled(addr string, test precompiledTest, bench *testing.B) {
res, err = RunPrecompiledContract(p, data, contract) res, err = RunPrecompiledContract(p, data, contract)
} }
bench.StopTimer() bench.StopTimer()
elapsed := float64(time.Now().Nanosecond() - start)
if elapsed < 1 {
elapsed = 1
}
gasUsed := reqGas * uint64(bench.N)
bench.ReportMetric(float64(reqGas), "gas/op") bench.ReportMetric(float64(reqGas), "gas/op")
bench.ReportMetric(float64(gasUsed*1000)/elapsed, "mgas/s")
//Check if it is correct //Check if it is correct
if err != nil { if err != nil {
bench.Error(err) bench.Error(err)
@ -320,3 +328,42 @@ func loadJsonFail(name string) ([]precompiledFailureTest, error) {
err = json.Unmarshal(data, &testcases) err = json.Unmarshal(data, &testcases)
return testcases, err return testcases, err
} }
// BenchmarkPrecompiledBLS12381G1MultiExpWorstCase benchmarks the worst case we could find that still fits a gaslimit of 10MGas.
func BenchmarkPrecompiledBLS12381G1MultiExpWorstCase(b *testing.B) {
task := "0000000000000000000000000000000008d8c4a16fb9d8800cce987c0eadbb6b3b005c213d44ecb5adeed713bae79d606041406df26169c35df63cf972c94be1" +
"0000000000000000000000000000000011bc8afe71676e6730702a46ef817060249cd06cd82e6981085012ff6d013aa4470ba3a2c71e13ef653e1e223d1ccfe9" +
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
input := task
for i := 0; i < 4787; i++ {
input = input + task
}
testcase := precompiledTest{
Input: input,
Expected: "0000000000000000000000000000000005a6310ea6f2a598023ae48819afc292b4dfcb40aabad24a0c2cb6c19769465691859eeb2a764342a810c5038d700f18000000000000000000000000000000001268ac944437d15923dc0aec00daa9250252e43e4b35ec7a19d01f0d6cd27f6e139d80dae16ba1c79cc7f57055a93ff5",
Name: "WorstCaseG1",
NoBenchmark: false,
}
benchmarkPrecompiled("0c", testcase, b)
}
// BenchmarkPrecompiledBLS12381G2MultiExpWorstCase benchmarks the worst case we could find that still fits a gaslimit of 10MGas.
func BenchmarkPrecompiledBLS12381G2MultiExpWorstCase(b *testing.B) {
task := "000000000000000000000000000000000d4f09acd5f362e0a516d4c13c5e2f504d9bd49fdfb6d8b7a7ab35a02c391c8112b03270d5d9eefe9b659dd27601d18f" +
"000000000000000000000000000000000fd489cb75945f3b5ebb1c0e326d59602934c8f78fe9294a8877e7aeb95de5addde0cb7ab53674df8b2cfbb036b30b99" +
"00000000000000000000000000000000055dbc4eca768714e098bbe9c71cf54b40f51c26e95808ee79225a87fb6fa1415178db47f02d856fea56a752d185f86b" +
"000000000000000000000000000000001239b7640f416eb6e921fe47f7501d504fadc190d9cf4e89ae2b717276739a2f4ee9f637c35e23c480df029fd8d247c7" +
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
input := task
for i := 0; i < 1040; i++ {
input = input + task
}
testcase := precompiledTest{
Input: input,
Expected: "0000000000000000000000000000000018f5ea0c8b086095cfe23f6bb1d90d45de929292006dba8cdedd6d3203af3c6bbfd592e93ecb2b2c81004961fdcbb46c00000000000000000000000000000000076873199175664f1b6493a43c02234f49dc66f077d3007823e0343ad92e30bd7dc209013435ca9f197aca44d88e9dac000000000000000000000000000000000e6f07f4b23b511eac1e2682a0fc224c15d80e122a3e222d00a41fab15eba645a700b9ae84f331ae4ed873678e2e6c9b000000000000000000000000000000000bcb4849e460612aaed79617255fd30c03f51cf03d2ed4163ca810c13e1954b1e8663157b957a601829bb272a4e6c7b8",
Name: "WorstCaseG2",
NoBenchmark: false,
}
benchmarkPrecompiled("0f", testcase, b)
}