github.com/core-coin/go-core/v2@v2.1.9/core/vm/contracts_test.go (about)

     1  // Copyright 2017 by the Authors
     2  // This file is part of the go-core library.
     3  //
     4  // The go-core library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-core library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-core library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package vm
    18  
    19  import (
    20  	"bytes"
    21  	"encoding/json"
    22  	"fmt"
    23  	"io/ioutil"
    24  	"testing"
    25  	"time"
    26  
    27  	"github.com/core-coin/go-core/v2/common"
    28  )
    29  
    30  // precompiledTest defines the input/output pairs for precompiled contract tests.
    31  type precompiledTest struct {
    32  	Input, Expected string
    33  	Energy          uint64
    34  	Name            string
    35  	NoBenchmark     bool // Benchmark primarily the worst-cases
    36  }
    37  
    38  // precompiledFailureTest defines the input/error pairs for precompiled
    39  // contract failure tests.
    40  type precompiledFailureTest struct {
    41  	Input         string
    42  	ExpectedError string
    43  	Name          string
    44  }
    45  
    46  // allPrecompiles does not map to the actual set of precompiles, as it also contains
    47  // repriced versions of precompiles at certain slots
    48  var allPrecompiles = map[common.Address]PrecompiledContract{
    49  	common.BytesToAddress([]byte{1}): &ecrecover{},
    50  	common.BytesToAddress([]byte{2}): &sha256hash{},
    51  	common.BytesToAddress([]byte{3}): &ripemd160hash{},
    52  	common.BytesToAddress([]byte{4}): &dataCopy{},
    53  	common.BytesToAddress([]byte{5}): &bigModExp{},
    54  	common.BytesToAddress([]byte{6}): &bn256Add{},
    55  	common.BytesToAddress([]byte{7}): &bn256ScalarMul{},
    56  	common.BytesToAddress([]byte{8}): &bn256Pairing{},
    57  	common.BytesToAddress([]byte{9}): &blake2F{},
    58  }
    59  
    60  // CIP-152 test vectors
    61  var blake2FMalformedInputTests = []precompiledFailureTest{
    62  	{
    63  		Input:         "",
    64  		ExpectedError: errBlake2FInvalidInputLength.Error(),
    65  		Name:          "vector 0: empty input",
    66  	},
    67  	{
    68  		Input:         "00000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001",
    69  		ExpectedError: errBlake2FInvalidInputLength.Error(),
    70  		Name:          "vector 1: less than 213 bytes input",
    71  	},
    72  	{
    73  		Input:         "000000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001",
    74  		ExpectedError: errBlake2FInvalidInputLength.Error(),
    75  		Name:          "vector 2: more than 213 bytes input",
    76  	},
    77  	{
    78  		Input:         "0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000002",
    79  		ExpectedError: errBlake2FInvalidFinalFlag.Error(),
    80  		Name:          "vector 3: malformed final block indicator flag",
    81  	},
    82  }
    83  
    84  func testPrecompiled(addrStr string, test precompiledTest, t *testing.T) {
    85  	addr, err := common.HexToAddress(addrStr)
    86  	if err != nil {
    87  		t.Error(err)
    88  	}
    89  	p := allPrecompiles[addr]
    90  	in := common.Hex2Bytes(test.Input)
    91  	energy := p.RequiredEnergy(in)
    92  	t.Run(fmt.Sprintf("%s-Energy=%d", test.Name, energy), func(t *testing.T) {
    93  		if res, _, err := RunPrecompiledContract(p, in, energy); err != nil {
    94  			if addrStr != "01" {
    95  				t.Error(err)
    96  			}
    97  		} else if common.Bytes2Hex(res) != test.Expected {
    98  			t.Errorf("Expected %v, got %v", test.Expected, common.Bytes2Hex(res))
    99  		}
   100  		if expEnergy := test.Energy; expEnergy != energy {
   101  			t.Errorf("%v: energy wrong, expected %d, got %d", test.Name, expEnergy, energy)
   102  		}
   103  		// Verify that the precompile did not touch the input buffer
   104  		exp := common.Hex2Bytes(test.Input)
   105  		if !bytes.Equal(in, exp) {
   106  			t.Errorf("Precompiled %v modified input data", addr)
   107  		}
   108  	})
   109  }
   110  
   111  func testPrecompiledOOG(addrStr string, test precompiledTest, t *testing.T) {
   112  	addr, err := common.HexToAddress(addrStr)
   113  	if err != nil {
   114  		t.Error(err)
   115  	}
   116  	p := allPrecompiles[addr]
   117  	in := common.Hex2Bytes(test.Input)
   118  	energy := p.RequiredEnergy(in) - 1
   119  
   120  	t.Run(fmt.Sprintf("%s-Energy=%d", test.Name, energy), func(t *testing.T) {
   121  		_, _, err := RunPrecompiledContract(p, in, energy)
   122  		if err.Error() != "out of energy" {
   123  			t.Errorf("Expected error [out of energy], got [%v]", err)
   124  		}
   125  		// Verify that the precompile did not touch the input buffer
   126  		exp := common.Hex2Bytes(test.Input)
   127  		if !bytes.Equal(in, exp) {
   128  			t.Errorf("Precompiled %v modified input data", addr)
   129  		}
   130  	})
   131  }
   132  
   133  func testPrecompiledFailure(addrStr string, test precompiledFailureTest, t *testing.T) {
   134  	addr, err := common.HexToAddress(addrStr)
   135  	if err != nil {
   136  		t.Error(err)
   137  	}
   138  	p := allPrecompiles[addr]
   139  	in := common.Hex2Bytes(test.Input)
   140  	energy := p.RequiredEnergy(in)
   141  	t.Run(test.Name, func(t *testing.T) {
   142  		_, _, err := RunPrecompiledContract(p, in, energy)
   143  		if err.Error() != test.ExpectedError {
   144  			t.Errorf("Expected error [%v], got [%v]", test.ExpectedError, err)
   145  		}
   146  		// Verify that the precompile did not touch the input buffer
   147  		exp := common.Hex2Bytes(test.Input)
   148  		if !bytes.Equal(in, exp) {
   149  			t.Errorf("Precompiled %v modified input data", addr)
   150  		}
   151  	})
   152  }
   153  
   154  func benchmarkPrecompiled(addrStr string, test precompiledTest, bench *testing.B) {
   155  	if test.NoBenchmark {
   156  		return
   157  	}
   158  	addr, err := common.HexToAddress(addrStr)
   159  	if err != nil {
   160  		bench.Error(err)
   161  	}
   162  	p := allPrecompiles[addr]
   163  	in := common.Hex2Bytes(test.Input)
   164  	reqEnergy := p.RequiredEnergy(in)
   165  
   166  	var (
   167  		res  []byte
   168  		data = make([]byte, len(in))
   169  	)
   170  
   171  	bench.Run(fmt.Sprintf("%s-Energy=%d", test.Name, reqEnergy), func(bench *testing.B) {
   172  		bench.ReportAllocs()
   173  		start := time.Now()
   174  		bench.ResetTimer()
   175  		for i := 0; i < bench.N; i++ {
   176  			copy(data, in)
   177  			res, _, err = RunPrecompiledContract(p, data, reqEnergy)
   178  		}
   179  		bench.StopTimer()
   180  		elapsed := uint64(time.Since(start))
   181  		if elapsed < 1 {
   182  			elapsed = 1
   183  		}
   184  		energyUsed := reqEnergy * uint64(bench.N)
   185  		bench.ReportMetric(float64(reqEnergy), "energy/op")
   186  		// Keep it as uint64, multiply 100 to get two digit float later
   187  		menergyps := (100 * 1000 * energyUsed) / elapsed
   188  		bench.ReportMetric(float64(menergyps)/100, "menergy/s")
   189  		//Check if it is correct
   190  		if err != nil {
   191  			bench.Error(err)
   192  			return
   193  		}
   194  		if common.Bytes2Hex(res) != test.Expected {
   195  			bench.Error(fmt.Sprintf("Expected %v, got %v", test.Expected, common.Bytes2Hex(res)))
   196  			return
   197  		}
   198  	})
   199  }
   200  
   201  // Benchmarks the sample inputs from the ECRECOVER precompile.
   202  func BenchmarkPrecompiledEcrecover(bench *testing.B) {
   203  	t := precompiledTest{
   204  		Input:    "38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e000000000000000000000000000000000000000000000000000000000000001b38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e789d1dd423d25f0772d2748d60f7e4b81bb14d086eba8e8e8efb6dcff8a4ae02",
   205  		Expected: "000000000000000000000000ceaccac640adf55b2028469bd36ba501f28b699d",
   206  		Name:     "",
   207  	}
   208  	benchmarkPrecompiled("01", t, bench)
   209  }
   210  
   211  // Benchmarks the sample inputs from the SHA256 precompile.
   212  func BenchmarkPrecompiledSha256(bench *testing.B) {
   213  	t := precompiledTest{
   214  		Input:    "38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e000000000000000000000000000000000000000000000000000000000000001b38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e789d1dd423d25f0772d2748d60f7e4b81bb14d086eba8e8e8efb6dcff8a4ae02",
   215  		Expected: "811c7003375852fabd0d362e40e68607a12bdabae61a7d068fe5fdd1dbbf2a5d",
   216  		Name:     "128",
   217  	}
   218  	benchmarkPrecompiled("02", t, bench)
   219  }
   220  
   221  // Benchmarks the sample inputs from the RIPEMD precompile.
   222  func BenchmarkPrecompiledRipeMD(bench *testing.B) {
   223  	t := precompiledTest{
   224  		Input:    "38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e000000000000000000000000000000000000000000000000000000000000001b38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e789d1dd423d25f0772d2748d60f7e4b81bb14d086eba8e8e8efb6dcff8a4ae02",
   225  		Expected: "0000000000000000000000009215b8d9882ff46f0dfde6684d78e831467f65e6",
   226  		Name:     "128",
   227  	}
   228  	benchmarkPrecompiled("03", t, bench)
   229  }
   230  
   231  // Benchmarks the sample inputs from the identiy precompile.
   232  func BenchmarkPrecompiledIdentity(bench *testing.B) {
   233  	t := precompiledTest{
   234  		Input:    "38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e000000000000000000000000000000000000000000000000000000000000001b38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e789d1dd423d25f0772d2748d60f7e4b81bb14d086eba8e8e8efb6dcff8a4ae02",
   235  		Expected: "38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e000000000000000000000000000000000000000000000000000000000000001b38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e789d1dd423d25f0772d2748d60f7e4b81bb14d086eba8e8e8efb6dcff8a4ae02",
   236  		Name:     "128",
   237  	}
   238  	benchmarkPrecompiled("04", t, bench)
   239  }
   240  
   241  // Tests the sample inputs from the ModExp CIP 198.
   242  func TestPrecompiledModExp(t *testing.T)      { testJson("modexp", "05", t) }
   243  func BenchmarkPrecompiledModExp(b *testing.B) { benchJson("modexp", "05", b) }
   244  
   245  // Tests the sample inputs from the elliptic curve addition CIP 213.
   246  func TestPrecompiledBn256Add(t *testing.T)      { testJson("bn256Add", "06", t) }
   247  func BenchmarkPrecompiledBn256Add(b *testing.B) { benchJson("bn256Add", "06", b) }
   248  
   249  // Tests OOG
   250  func TestPrecompiledModExpOOG(t *testing.T) {
   251  	modexpTests, err := loadJson("modexp")
   252  	if err != nil {
   253  		t.Fatal(err)
   254  	}
   255  	for _, test := range modexpTests {
   256  		testPrecompiledOOG("05", test, t)
   257  	}
   258  }
   259  
   260  // Tests the sample inputs from the elliptic curve scalar multiplication CIP 213.
   261  func TestPrecompiledBn256ScalarMul(t *testing.T)      { testJson("bn256ScalarMul", "07", t) }
   262  func BenchmarkPrecompiledBn256ScalarMul(b *testing.B) { benchJson("bn256ScalarMul", "07", b) }
   263  
   264  // Tests the sample inputs from the elliptic curve pairing check CIP 197.
   265  func TestPrecompiledBn256Pairing(t *testing.T)      { testJson("bn256Pairing", "08", t) }
   266  func BenchmarkPrecompiledBn256Pairing(b *testing.B) { benchJson("bn256Pairing", "08", b) }
   267  
   268  func TestPrecompiledBlake2F(t *testing.T)      { testJson("blake2F", "09", t) }
   269  func BenchmarkPrecompiledBlake2F(b *testing.B) { benchJson("blake2F", "09", b) }
   270  
   271  func TestPrecompileBlake2FMalformedInput(t *testing.T) {
   272  	for _, test := range blake2FMalformedInputTests {
   273  		testPrecompiledFailure("09", test, t)
   274  	}
   275  }
   276  
   277  func TestPrecompiledEcrecover(t *testing.T) {
   278  	testJson("ecRecover", "01", t)
   279  }
   280  
   281  func testJson(name, addr string, t *testing.T) {
   282  	tests, err := loadJson(name)
   283  	if err != nil {
   284  		t.Fatal(err)
   285  	}
   286  	for _, test := range tests {
   287  		testPrecompiled(addr, test, t)
   288  	}
   289  }
   290  
   291  func benchJson(name, addr string, b *testing.B) {
   292  	tests, err := loadJson(name)
   293  	if err != nil {
   294  		b.Fatal(err)
   295  	}
   296  	for _, test := range tests {
   297  		benchmarkPrecompiled(addr, test, b)
   298  	}
   299  }
   300  
   301  func loadJson(name string) ([]precompiledTest, error) {
   302  	data, err := ioutil.ReadFile(fmt.Sprintf("testdata/precompiles/%v.json", name))
   303  	if err != nil {
   304  		return nil, err
   305  	}
   306  	var testcases []precompiledTest
   307  	err = json.Unmarshal(data, &testcases)
   308  	return testcases, err
   309  }