diff --git a/tests/antithesis/main.go b/tests/antithesis/main.go index 18e6419a14..92c1579ad0 100644 --- a/tests/antithesis/main.go +++ b/tests/antithesis/main.go @@ -10,6 +10,7 @@ import ( "fmt" "math/big" "path/filepath" + "runtime" "time" "github.com/antithesishq/antithesis-sdk-go/assert" @@ -26,7 +27,6 @@ import ( "github.com/ava-labs/subnet-evm/accounts/abi/bind" "github.com/ava-labs/subnet-evm/ethclient" - "github.com/ava-labs/subnet-evm/tests" "github.com/ava-labs/subnet-evm/tests/utils" ago_tests "github.com/ava-labs/avalanchego/tests" @@ -51,8 +51,9 @@ func main() { tmpnet.FlagsMap{}, ), func(nodes ...*tmpnet.Node) []*tmpnet.Subnet { - repoRootPath := tests.GetRepoRootPath("tests/antithesis") - genesisPath := filepath.Join(repoRootPath, "tests/load/genesis/genesis.json") + _, thisFile, _, _ := runtime.Caller(0) + testsDir := filepath.Dir(filepath.Dir(thisFile)) // tests/antithesis/ -> tests/ + genesisPath := filepath.Join(testsDir, "load", "genesis", "genesis.json") return []*tmpnet.Subnet{ utils.NewTmpnetSubnet("subnet-evm", genesisPath, utils.DefaultChainConfig, nodes...), } diff --git a/tests/gen_stenv.go b/tests/gen_stenv.go deleted file mode 100644 index 96d56d3007..0000000000 --- a/tests/gen_stenv.go +++ /dev/null @@ -1,85 +0,0 @@ -// Code generated by github.com/fjl/gencodec. DO NOT EDIT. - -package tests - -import ( - "encoding/json" - "errors" - "math/big" - - "github.com/ava-labs/libevm/common" - "github.com/ava-labs/libevm/common/math" -) - -var _ = (*stEnvMarshaling)(nil) - -// MarshalJSON marshals as JSON. -func (s stEnv) MarshalJSON() ([]byte, error) { - type stEnv struct { - Coinbase common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"` - Difficulty *math.HexOrDecimal256 `json:"currentDifficulty" gencodec:"optional"` - Random *math.HexOrDecimal256 `json:"currentRandom" gencodec:"optional"` - GasLimit math.HexOrDecimal64 `json:"currentGasLimit" gencodec:"required"` - Number math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"` - Timestamp math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"` - BaseFee *math.HexOrDecimal256 `json:"currentBaseFee" gencodec:"optional"` - ExcessBlobGas *math.HexOrDecimal64 `json:"currentExcessBlobGas" gencodec:"optional"` - } - var enc stEnv - enc.Coinbase = common.UnprefixedAddress(s.Coinbase) - enc.Difficulty = (*math.HexOrDecimal256)(s.Difficulty) - enc.Random = (*math.HexOrDecimal256)(s.Random) - enc.GasLimit = math.HexOrDecimal64(s.GasLimit) - enc.Number = math.HexOrDecimal64(s.Number) - enc.Timestamp = math.HexOrDecimal64(s.Timestamp) - enc.BaseFee = (*math.HexOrDecimal256)(s.BaseFee) - enc.ExcessBlobGas = (*math.HexOrDecimal64)(s.ExcessBlobGas) - return json.Marshal(&enc) -} - -// UnmarshalJSON unmarshals from JSON. -func (s *stEnv) UnmarshalJSON(input []byte) error { - type stEnv struct { - Coinbase *common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"` - Difficulty *math.HexOrDecimal256 `json:"currentDifficulty" gencodec:"optional"` - Random *math.HexOrDecimal256 `json:"currentRandom" gencodec:"optional"` - GasLimit *math.HexOrDecimal64 `json:"currentGasLimit" gencodec:"required"` - Number *math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"` - Timestamp *math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"` - BaseFee *math.HexOrDecimal256 `json:"currentBaseFee" gencodec:"optional"` - ExcessBlobGas *math.HexOrDecimal64 `json:"currentExcessBlobGas" gencodec:"optional"` - } - var dec stEnv - if err := json.Unmarshal(input, &dec); err != nil { - return err - } - if dec.Coinbase == nil { - return errors.New("missing required field 'currentCoinbase' for stEnv") - } - s.Coinbase = common.Address(*dec.Coinbase) - if dec.Difficulty != nil { - s.Difficulty = (*big.Int)(dec.Difficulty) - } - if dec.Random != nil { - s.Random = (*big.Int)(dec.Random) - } - if dec.GasLimit == nil { - return errors.New("missing required field 'currentGasLimit' for stEnv") - } - s.GasLimit = uint64(*dec.GasLimit) - if dec.Number == nil { - return errors.New("missing required field 'currentNumber' for stEnv") - } - s.Number = uint64(*dec.Number) - if dec.Timestamp == nil { - return errors.New("missing required field 'currentTimestamp' for stEnv") - } - s.Timestamp = uint64(*dec.Timestamp) - if dec.BaseFee != nil { - s.BaseFee = (*big.Int)(dec.BaseFee) - } - if dec.ExcessBlobGas != nil { - s.ExcessBlobGas = (*uint64)(dec.ExcessBlobGas) - } - return nil -} diff --git a/tests/gen_sttransaction.go b/tests/gen_sttransaction.go deleted file mode 100644 index 7a98bd6476..0000000000 --- a/tests/gen_sttransaction.go +++ /dev/null @@ -1,120 +0,0 @@ -// Code generated by github.com/fjl/gencodec. DO NOT EDIT. - -package tests - -import ( - "encoding/json" - "math/big" - - "github.com/ava-labs/libevm/common" - "github.com/ava-labs/libevm/common/hexutil" - "github.com/ava-labs/libevm/common/math" - "github.com/ava-labs/libevm/core/types" -) - -var _ = (*stTransactionMarshaling)(nil) - -// MarshalJSON marshals as JSON. -func (s stTransaction) MarshalJSON() ([]byte, error) { - type stTransaction struct { - GasPrice *math.HexOrDecimal256 `json:"gasPrice"` - MaxFeePerGas *math.HexOrDecimal256 `json:"maxFeePerGas"` - MaxPriorityFeePerGas *math.HexOrDecimal256 `json:"maxPriorityFeePerGas"` - Nonce math.HexOrDecimal64 `json:"nonce"` - To string `json:"to"` - Data []string `json:"data"` - AccessLists []*types.AccessList `json:"accessLists,omitempty"` - GasLimit []math.HexOrDecimal64 `json:"gasLimit"` - Value []string `json:"value"` - PrivateKey hexutil.Bytes `json:"secretKey"` - Sender *common.Address `json:"sender"` - BlobVersionedHashes []common.Hash `json:"blobVersionedHashes,omitempty"` - BlobGasFeeCap *math.HexOrDecimal256 `json:"maxFeePerBlobGas,omitempty"` - } - var enc stTransaction - enc.GasPrice = (*math.HexOrDecimal256)(s.GasPrice) - enc.MaxFeePerGas = (*math.HexOrDecimal256)(s.MaxFeePerGas) - enc.MaxPriorityFeePerGas = (*math.HexOrDecimal256)(s.MaxPriorityFeePerGas) - enc.Nonce = math.HexOrDecimal64(s.Nonce) - enc.To = s.To - enc.Data = s.Data - enc.AccessLists = s.AccessLists - if s.GasLimit != nil { - enc.GasLimit = make([]math.HexOrDecimal64, len(s.GasLimit)) - for k, v := range s.GasLimit { - enc.GasLimit[k] = math.HexOrDecimal64(v) - } - } - enc.Value = s.Value - enc.PrivateKey = s.PrivateKey - enc.Sender = s.Sender - enc.BlobVersionedHashes = s.BlobVersionedHashes - enc.BlobGasFeeCap = (*math.HexOrDecimal256)(s.BlobGasFeeCap) - return json.Marshal(&enc) -} - -// UnmarshalJSON unmarshals from JSON. -func (s *stTransaction) UnmarshalJSON(input []byte) error { - type stTransaction struct { - GasPrice *math.HexOrDecimal256 `json:"gasPrice"` - MaxFeePerGas *math.HexOrDecimal256 `json:"maxFeePerGas"` - MaxPriorityFeePerGas *math.HexOrDecimal256 `json:"maxPriorityFeePerGas"` - Nonce *math.HexOrDecimal64 `json:"nonce"` - To *string `json:"to"` - Data []string `json:"data"` - AccessLists []*types.AccessList `json:"accessLists,omitempty"` - GasLimit []math.HexOrDecimal64 `json:"gasLimit"` - Value []string `json:"value"` - PrivateKey *hexutil.Bytes `json:"secretKey"` - Sender *common.Address `json:"sender"` - BlobVersionedHashes []common.Hash `json:"blobVersionedHashes,omitempty"` - BlobGasFeeCap *math.HexOrDecimal256 `json:"maxFeePerBlobGas,omitempty"` - } - var dec stTransaction - if err := json.Unmarshal(input, &dec); err != nil { - return err - } - if dec.GasPrice != nil { - s.GasPrice = (*big.Int)(dec.GasPrice) - } - if dec.MaxFeePerGas != nil { - s.MaxFeePerGas = (*big.Int)(dec.MaxFeePerGas) - } - if dec.MaxPriorityFeePerGas != nil { - s.MaxPriorityFeePerGas = (*big.Int)(dec.MaxPriorityFeePerGas) - } - if dec.Nonce != nil { - s.Nonce = uint64(*dec.Nonce) - } - if dec.To != nil { - s.To = *dec.To - } - if dec.Data != nil { - s.Data = dec.Data - } - if dec.AccessLists != nil { - s.AccessLists = dec.AccessLists - } - if dec.GasLimit != nil { - s.GasLimit = make([]uint64, len(dec.GasLimit)) - for k, v := range dec.GasLimit { - s.GasLimit[k] = uint64(v) - } - } - if dec.Value != nil { - s.Value = dec.Value - } - if dec.PrivateKey != nil { - s.PrivateKey = *dec.PrivateKey - } - if dec.Sender != nil { - s.Sender = dec.Sender - } - if dec.BlobVersionedHashes != nil { - s.BlobVersionedHashes = dec.BlobVersionedHashes - } - if dec.BlobGasFeeCap != nil { - s.BlobGasFeeCap = (*big.Int)(dec.BlobGasFeeCap) - } - return nil -} diff --git a/tests/init.go b/tests/init.go deleted file mode 100644 index a76703e909..0000000000 --- a/tests/init.go +++ /dev/null @@ -1,293 +0,0 @@ -// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. -// -// This file is a derived work, based on the go-ethereum library whose original -// notices appear below. -// -// It is distributed under a license compatible with the licensing terms of the -// original code from which it is derived. -// -// Much love to the original authors for their work. -// ********** -// Copyright 2015 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 tests - -import ( - "fmt" - "math/big" - "os" - "sort" - "strings" - - "github.com/ava-labs/libevm/libevm" - "github.com/ava-labs/subnet-evm/params" - "github.com/ava-labs/subnet-evm/params/extras" - "github.com/ava-labs/subnet-evm/utils" -) - -// Forks table defines supported forks and their chain config. -var Forks map[string]*params.ChainConfig - -func init() { - libevm.WithTemporaryExtrasLock(func(l libevm.ExtrasLock) error { - return params.WithTempRegisteredExtras(l, func() error { - initializeForks() - return nil - }) - }) -} - -// initializeForks MUST be called inside [params.WithTempRegisteredExtras] to -// allow [params.WithExtra] to work without global registration of libevm -// extras. -func initializeForks() { - Forks = map[string]*params.ChainConfig{ - "Frontier": { - ChainID: big.NewInt(1), - }, - "Homestead": { - ChainID: big.NewInt(1), - HomesteadBlock: big.NewInt(0), - }, - "EIP150": { - ChainID: big.NewInt(1), - HomesteadBlock: big.NewInt(0), - EIP150Block: big.NewInt(0), - }, - "EIP158": { - ChainID: big.NewInt(1), - HomesteadBlock: big.NewInt(0), - EIP150Block: big.NewInt(0), - EIP155Block: big.NewInt(0), - EIP158Block: big.NewInt(0), - }, - "Byzantium": { - ChainID: big.NewInt(1), - HomesteadBlock: big.NewInt(0), - EIP150Block: big.NewInt(0), - EIP155Block: big.NewInt(0), - EIP158Block: big.NewInt(0), - ByzantiumBlock: big.NewInt(0), - }, - "Constantinople": { - ChainID: big.NewInt(1), - HomesteadBlock: big.NewInt(0), - EIP150Block: big.NewInt(0), - EIP155Block: big.NewInt(0), - EIP158Block: big.NewInt(0), - ByzantiumBlock: big.NewInt(0), - ConstantinopleBlock: big.NewInt(0), - PetersburgBlock: big.NewInt(10000000), - }, - "ConstantinopleFix": { - ChainID: big.NewInt(1), - HomesteadBlock: big.NewInt(0), - EIP150Block: big.NewInt(0), - EIP155Block: big.NewInt(0), - EIP158Block: big.NewInt(0), - ByzantiumBlock: big.NewInt(0), - ConstantinopleBlock: big.NewInt(0), - PetersburgBlock: big.NewInt(0), - }, - "Istanbul": { - ChainID: big.NewInt(1), - HomesteadBlock: big.NewInt(0), - EIP150Block: big.NewInt(0), - EIP155Block: big.NewInt(0), - EIP158Block: big.NewInt(0), - ByzantiumBlock: big.NewInt(0), - ConstantinopleBlock: big.NewInt(0), - PetersburgBlock: big.NewInt(0), - IstanbulBlock: big.NewInt(0), - }, - "MuirGlacier": { - ChainID: big.NewInt(1), - HomesteadBlock: big.NewInt(0), - EIP150Block: big.NewInt(0), - EIP155Block: big.NewInt(0), - EIP158Block: big.NewInt(0), - ByzantiumBlock: big.NewInt(0), - ConstantinopleBlock: big.NewInt(0), - PetersburgBlock: big.NewInt(0), - IstanbulBlock: big.NewInt(0), - MuirGlacierBlock: big.NewInt(0), - }, - "FrontierToHomesteadAt5": { - ChainID: big.NewInt(1), - HomesteadBlock: big.NewInt(5), - }, - "HomesteadToEIP150At5": { - ChainID: big.NewInt(1), - HomesteadBlock: big.NewInt(0), - EIP150Block: big.NewInt(5), - }, - "EIP158ToByzantiumAt5": { - ChainID: big.NewInt(1), - HomesteadBlock: big.NewInt(0), - EIP150Block: big.NewInt(0), - EIP155Block: big.NewInt(0), - EIP158Block: big.NewInt(0), - ByzantiumBlock: big.NewInt(5), - }, - "ByzantiumToConstantinopleAt5": { - ChainID: big.NewInt(1), - HomesteadBlock: big.NewInt(0), - EIP150Block: big.NewInt(0), - EIP155Block: big.NewInt(0), - EIP158Block: big.NewInt(0), - ByzantiumBlock: big.NewInt(0), - ConstantinopleBlock: big.NewInt(5), - }, - "ByzantiumToConstantinopleFixAt5": { - ChainID: big.NewInt(1), - HomesteadBlock: big.NewInt(0), - EIP150Block: big.NewInt(0), - EIP155Block: big.NewInt(0), - EIP158Block: big.NewInt(0), - ByzantiumBlock: big.NewInt(0), - ConstantinopleBlock: big.NewInt(5), - PetersburgBlock: big.NewInt(5), - }, - "ConstantinopleFixToIstanbulAt5": { - ChainID: big.NewInt(1), - HomesteadBlock: big.NewInt(0), - EIP150Block: big.NewInt(0), - EIP155Block: big.NewInt(0), - EIP158Block: big.NewInt(0), - ByzantiumBlock: big.NewInt(0), - ConstantinopleBlock: big.NewInt(0), - PetersburgBlock: big.NewInt(0), - IstanbulBlock: big.NewInt(5), - }, - "Pre-SubnetEVM": { - ChainID: big.NewInt(1), - HomesteadBlock: big.NewInt(0), - EIP150Block: big.NewInt(0), - EIP155Block: big.NewInt(0), - EIP158Block: big.NewInt(0), - ByzantiumBlock: big.NewInt(0), - ConstantinopleBlock: big.NewInt(0), - PetersburgBlock: big.NewInt(0), - IstanbulBlock: big.NewInt(0), - MuirGlacierBlock: big.NewInt(0), - }, - "SubnetEVM": params.WithExtra( - ¶ms.ChainConfig{ - ChainID: big.NewInt(1), - HomesteadBlock: big.NewInt(0), - EIP150Block: big.NewInt(0), - EIP155Block: big.NewInt(0), - EIP158Block: big.NewInt(0), - ByzantiumBlock: big.NewInt(0), - ConstantinopleBlock: big.NewInt(0), - PetersburgBlock: big.NewInt(0), - IstanbulBlock: big.NewInt(0), - BerlinBlock: big.NewInt(0), - LondonBlock: big.NewInt(0), - }, - &extras.ChainConfig{ - NetworkUpgrades: extras.NetworkUpgrades{ - SubnetEVMTimestamp: utils.NewUint64(0), - }, - }, - ), - "Durango": params.WithExtra( - ¶ms.ChainConfig{ - ChainID: big.NewInt(1), - HomesteadBlock: big.NewInt(0), - EIP150Block: big.NewInt(0), - EIP155Block: big.NewInt(0), - EIP158Block: big.NewInt(0), - ByzantiumBlock: big.NewInt(0), - ConstantinopleBlock: big.NewInt(0), - PetersburgBlock: big.NewInt(0), - IstanbulBlock: big.NewInt(0), - BerlinBlock: big.NewInt(0), - LondonBlock: big.NewInt(0), - }, - &extras.ChainConfig{ - NetworkUpgrades: extras.NetworkUpgrades{ - SubnetEVMTimestamp: utils.NewUint64(0), - DurangoTimestamp: utils.NewUint64(0), - }, - }, - ), - "Cancun": params.WithExtra( - ¶ms.ChainConfig{ - ChainID: big.NewInt(1), - HomesteadBlock: big.NewInt(0), - EIP150Block: big.NewInt(0), - EIP155Block: big.NewInt(0), - EIP158Block: big.NewInt(0), - ByzantiumBlock: big.NewInt(0), - ConstantinopleBlock: big.NewInt(0), - PetersburgBlock: big.NewInt(0), - IstanbulBlock: big.NewInt(0), - BerlinBlock: big.NewInt(0), - LondonBlock: big.NewInt(0), - ShanghaiTime: utils.NewUint64(0), - CancunTime: utils.NewUint64(0), - }, - &extras.ChainConfig{ - NetworkUpgrades: extras.NetworkUpgrades{ - SubnetEVMTimestamp: utils.NewUint64(0), - DurangoTimestamp: utils.NewUint64(0), - }, - }, - ), - } -} - -// AvailableForks returns the set of defined fork names -func AvailableForks() []string { - var availableForks []string - for k := range Forks { - availableForks = append(availableForks, k) - } - sort.Strings(availableForks) - return availableForks -} - -// UnsupportedForkError is returned when a test requests a fork that isn't implemented. -type UnsupportedForkError struct { - Name string -} - -func (e UnsupportedForkError) Error() string { - return fmt.Sprintf("unsupported fork %q", e.Name) -} - -func GetRepoRootPath(suffix string) string { - // - When executed via a test binary, the working directory will be wherever - // the binary is executed from, but scripts should require execution from - // the repo root. - // - // - When executed via ginkgo (nicer for development + supports - // parallel execution) the working directory will always be the - // target path (e.g. [repo root]./tests/warp) and getting the repo - // root will require stripping the target path suffix. - // - // TODO(marun) Avoid relying on the current working directory to find test - // dependencies by embedding data where possible (e.g. for genesis) and - // explicitly configuring paths for execution. - cwd, err := os.Getwd() - if err != nil { - panic(err) - } - return strings.TrimSuffix(cwd, suffix) -} diff --git a/tests/load/load_test.go b/tests/load/load_test.go index bd947dd6c5..08f29553e7 100644 --- a/tests/load/load_test.go +++ b/tests/load/load_test.go @@ -8,6 +8,7 @@ import ( "os" "os/exec" "path/filepath" + "runtime" "strings" "testing" @@ -18,7 +19,6 @@ import ( "github.com/ava-labs/libevm/log" "github.com/stretchr/testify/require" - "github.com/ava-labs/subnet-evm/tests" "github.com/ava-labs/subnet-evm/tests/utils" ginkgo "github.com/onsi/ginkgo/v2" @@ -32,8 +32,13 @@ const ( ) var ( - flagVars *e2e.FlagVars - repoRootPath = tests.GetRepoRootPath("tests/load") + flagVars *e2e.FlagVars + + thisDir, repoRoot = func() (string, string) { + _, thisFile, _, _ := runtime.Caller(0) + dir := filepath.Dir(thisFile) + return dir, filepath.Dir(filepath.Dir(dir)) + }() ) func init() { @@ -52,7 +57,7 @@ var _ = ginkgo.Describe("[Load Simulator]", ginkgo.Ordered, func() { ginkgo.BeforeAll(func() { tc := e2e.NewTestContext() - genesisPath := filepath.Join(repoRootPath, "tests/load/genesis/genesis.json") + genesisPath := filepath.Join(thisDir, "genesis", "genesis.json") nodes := utils.NewTmpnetNodes(nodeCount) env = e2e.NewTestEnvironment( @@ -89,7 +94,7 @@ var _ = ginkgo.Describe("[Load Simulator]", ginkgo.Ordered, func() { log.Info("Running load simulator...", "rpcEndpoints", commaSeparatedRPCEndpoints) cmd := exec.Command("./scripts/run_simulator.sh") - cmd.Dir = repoRootPath + cmd.Dir = repoRoot log.Info("Running load simulator script", "cmd", cmd.String()) out, err := cmd.CombinedOutput() diff --git a/tests/rlp_test_util.go b/tests/rlp_test_util.go deleted file mode 100644 index b0dd078581..0000000000 --- a/tests/rlp_test_util.go +++ /dev/null @@ -1,183 +0,0 @@ -// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. -// -// This file is a derived work, based on the go-ethereum library whose original -// notices appear below. -// -// It is distributed under a license compatible with the licensing terms of the -// original code from which it is derived. -// -// Much love to the original authors for their work. -// ********** -// Copyright 2015 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 tests - -import ( - "bytes" - "encoding/hex" - "errors" - "fmt" - "math/big" - "strings" - - "github.com/ava-labs/libevm/rlp" -) - -// RLPTest is the JSON structure of a single RLP test. -type RLPTest struct { - // If the value of In is "INVALID" or "VALID", the test - // checks whether Out can be decoded into a value of - // type interface{}. - // - // For other JSON values, In is treated as a driver for - // calls to rlp.Stream. The test also verifies that encoding - // In produces the bytes in Out. - In interface{} - - // Out is a hex-encoded RLP value. - Out string -} - -// FromHex returns the bytes represented by the hexadecimal string s. -// s may be prefixed with "0x". -// This is copy-pasted from bytes.go, which does not return the error -func FromHex(s string) ([]byte, error) { - if len(s) > 1 && (s[0:2] == "0x" || s[0:2] == "0X") { - s = s[2:] - } - if len(s)%2 == 1 { - s = "0" + s - } - return hex.DecodeString(s) -} - -// Run executes the test. -func (t *RLPTest) Run() error { - outb, err := FromHex(t.Out) - if err != nil { - return errors.New("invalid hex in Out") - } - - // Handle simple decoding tests with no actual In value. - if t.In == "VALID" || t.In == "INVALID" { - return checkDecodeInterface(outb, t.In == "VALID") - } - - // Check whether encoding the value produces the same bytes. - in := translateJSON(t.In) - b, err := rlp.EncodeToBytes(in) - if err != nil { - return fmt.Errorf("encode failed: %v", err) - } - if !bytes.Equal(b, outb) { - return fmt.Errorf("encode produced %x, want %x", b, outb) - } - // Test stream decoding. - s := rlp.NewStream(bytes.NewReader(outb), 0) - return checkDecodeFromJSON(s, in) -} - -func checkDecodeInterface(b []byte, isValid bool) error { - err := rlp.DecodeBytes(b, new(interface{})) - switch { - case isValid && err != nil: - return fmt.Errorf("decoding failed: %v", err) - case !isValid && err == nil: - return errors.New("decoding of invalid value succeeded") - } - return nil -} - -// translateJSON makes test json values encodable with RLP. -func translateJSON(v interface{}) interface{} { - switch v := v.(type) { - case float64: - return uint64(v) - case string: - if len(v) > 0 && v[0] == '#' { // # starts a faux big int. - big, ok := new(big.Int).SetString(v[1:], 10) - if !ok { - panic(fmt.Errorf("bad test: bad big int: %q", v)) - } - return big - } - return []byte(v) - case []interface{}: - new := make([]interface{}, len(v)) - for i := range v { - new[i] = translateJSON(v[i]) - } - return new - default: - panic(fmt.Errorf("can't handle %T", v)) - } -} - -// checkDecodeFromJSON decodes from s guided by exp. exp drives the -// Stream by invoking decoding operations (Uint, Big, List, ...) based -// on the type of each value. The value decoded from the RLP stream -// must match the JSON value. -func checkDecodeFromJSON(s *rlp.Stream, exp interface{}) error { - switch exp := exp.(type) { - case uint64: - i, err := s.Uint64() - if err != nil { - return addStack("Uint", exp, err) - } - if i != exp { - return addStack("Uint", exp, fmt.Errorf("result mismatch: got %d", i)) - } - case *big.Int: - big := new(big.Int) - if err := s.Decode(&big); err != nil { - return addStack("Big", exp, err) - } - if big.Cmp(exp) != 0 { - return addStack("Big", exp, fmt.Errorf("result mismatch: got %d", big)) - } - case []byte: - b, err := s.Bytes() - if err != nil { - return addStack("Bytes", exp, err) - } - if !bytes.Equal(b, exp) { - return addStack("Bytes", exp, fmt.Errorf("result mismatch: got %x", b)) - } - case []interface{}: - if _, err := s.List(); err != nil { - return addStack("List", exp, err) - } - for i, v := range exp { - if err := checkDecodeFromJSON(s, v); err != nil { - return addStack(fmt.Sprintf("[%d]", i), exp, err) - } - } - if err := s.ListEnd(); err != nil { - return addStack("ListEnd", exp, err) - } - default: - panic(fmt.Errorf("unhandled type: %T", exp)) - } - return nil -} - -func addStack(op string, val interface{}, err error) error { - lines := strings.Split(err.Error(), "\n") - lines = append(lines, fmt.Sprintf("\t%s: %v", op, val)) - return errors.New(strings.Join(lines, "\n")) -} diff --git a/tests/state_test_util.go b/tests/state_test_util.go index eb8326c6aa..4ff12a1b22 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -28,434 +28,25 @@ package tests import ( - "encoding/hex" - "encoding/json" - "errors" - "fmt" - "math/big" "os" - "strconv" - "strings" "github.com/ava-labs/libevm/common" - "github.com/ava-labs/libevm/common/hexutil" - "github.com/ava-labs/libevm/common/math" - "github.com/ava-labs/libevm/consensus/misc/eip4844" "github.com/ava-labs/libevm/core/rawdb" "github.com/ava-labs/libevm/core/state" "github.com/ava-labs/libevm/core/types" - "github.com/ava-labs/libevm/core/vm" - "github.com/ava-labs/libevm/crypto" "github.com/ava-labs/libevm/ethdb" - ethparams "github.com/ava-labs/libevm/params" - "github.com/ava-labs/libevm/rlp" "github.com/ava-labs/libevm/triedb" - "github.com/ava-labs/subnet-evm/core" "github.com/ava-labs/subnet-evm/core/extstate" "github.com/ava-labs/subnet-evm/core/state/snapshot" - "github.com/ava-labs/subnet-evm/params" "github.com/ava-labs/subnet-evm/plugin/evm/customrawdb" "github.com/ava-labs/subnet-evm/triedb/firewood" "github.com/ava-labs/subnet-evm/triedb/hashdb" "github.com/ava-labs/subnet-evm/triedb/pathdb" "github.com/holiman/uint256" - "golang.org/x/crypto/sha3" ) -// StateTest checks transaction processing without block context. -// See https://github.com/ethereum/EIPs/issues/176 for the test format specification. -type StateTest struct { - json stJSON -} - -// StateSubtest selects a specific configuration of a General State Test. -type StateSubtest struct { - Fork string - Index int -} - -func (t *StateTest) UnmarshalJSON(in []byte) error { - return json.Unmarshal(in, &t.json) -} - -type stJSON struct { - Env stEnv `json:"env"` - Pre types.GenesisAlloc `json:"pre"` - Tx stTransaction `json:"transaction"` - Out hexutil.Bytes `json:"out"` - Post map[string][]stPostState `json:"post"` -} - -type stPostState struct { - Root common.UnprefixedHash `json:"hash"` - Logs common.UnprefixedHash `json:"logs"` - TxBytes hexutil.Bytes `json:"txbytes"` - ExpectException string `json:"expectException"` - Indexes struct { - Data int `json:"data"` - Gas int `json:"gas"` - Value int `json:"value"` - } -} - -//go:generate go tool -modfile=../tools/go.mod gencodec -type stEnv -field-override stEnvMarshaling -out gen_stenv.go -type stEnv struct { - Coinbase common.Address `json:"currentCoinbase" gencodec:"required"` - Difficulty *big.Int `json:"currentDifficulty" gencodec:"optional"` - Random *big.Int `json:"currentRandom" gencodec:"optional"` - GasLimit uint64 `json:"currentGasLimit" gencodec:"required"` - Number uint64 `json:"currentNumber" gencodec:"required"` - Timestamp uint64 `json:"currentTimestamp" gencodec:"required"` - BaseFee *big.Int `json:"currentBaseFee" gencodec:"optional"` - ExcessBlobGas *uint64 `json:"currentExcessBlobGas" gencodec:"optional"` -} - -type stEnvMarshaling struct { - Coinbase common.UnprefixedAddress - Difficulty *math.HexOrDecimal256 - Random *math.HexOrDecimal256 - GasLimit math.HexOrDecimal64 - Number math.HexOrDecimal64 - Timestamp math.HexOrDecimal64 - BaseFee *math.HexOrDecimal256 - ExcessBlobGas *math.HexOrDecimal64 -} - -//go:generate go tool -modfile=../tools/go.mod gencodec -type stTransaction -field-override stTransactionMarshaling -out gen_sttransaction.go -type stTransaction struct { - GasPrice *big.Int `json:"gasPrice"` - MaxFeePerGas *big.Int `json:"maxFeePerGas"` - MaxPriorityFeePerGas *big.Int `json:"maxPriorityFeePerGas"` - Nonce uint64 `json:"nonce"` - To string `json:"to"` - Data []string `json:"data"` - AccessLists []*types.AccessList `json:"accessLists,omitempty"` - GasLimit []uint64 `json:"gasLimit"` - Value []string `json:"value"` - PrivateKey []byte `json:"secretKey"` - Sender *common.Address `json:"sender"` - BlobVersionedHashes []common.Hash `json:"blobVersionedHashes,omitempty"` - BlobGasFeeCap *big.Int `json:"maxFeePerBlobGas,omitempty"` -} - -// nolint: unused -type stTransactionMarshaling struct { - GasPrice *math.HexOrDecimal256 - MaxFeePerGas *math.HexOrDecimal256 - MaxPriorityFeePerGas *math.HexOrDecimal256 - Nonce math.HexOrDecimal64 - GasLimit []math.HexOrDecimal64 - PrivateKey hexutil.Bytes - BlobGasFeeCap *math.HexOrDecimal256 -} - -// GetChainConfig takes a fork definition and returns a chain config. -// The fork definition can be -// - a plain forkname, e.g. `Byzantium`, -// - a fork basename, and a list of EIPs to enable; e.g. `Byzantium+1884+1283`. -func GetChainConfig(forkString string) (baseConfig *params.ChainConfig, eips []int, err error) { - var ( - splitForks = strings.Split(forkString, "+") - ok bool - baseName, eipsStrings = splitForks[0], splitForks[1:] - ) - - // NOTE: this is added to support mapping geth fork names to - // subnet-evm fork names. - forkAliases := map[string]string{ - "Berlin": "Pre-SubnetEVM", - "London": "SubnetEVM", - "ArrowGlacier": "SubnetEVM", - "GrayGlacier": "SubnetEVM", - "Merge": "SubnetEVM", - } - if alias, ok := forkAliases[baseName]; ok { - baseName = alias - } - - if baseConfig, ok = Forks[baseName]; !ok { - return nil, nil, UnsupportedForkError{baseName} - } - for _, eip := range eipsStrings { - if eipNum, err := strconv.Atoi(eip); err != nil { - return nil, nil, fmt.Errorf("syntax error, invalid eip number %v", eipNum) - } else { - if !vm.ValidEip(eipNum) { - return nil, nil, fmt.Errorf("syntax error, invalid eip number %v", eipNum) - } - eips = append(eips, eipNum) - } - } - return baseConfig, eips, nil -} - -// Subtests returns all valid subtests of the test. -func (t *StateTest) Subtests() []StateSubtest { - var sub []StateSubtest - for fork, pss := range t.json.Post { - for i := range pss { - sub = append(sub, StateSubtest{fork, i}) - } - } - return sub -} - -// checkError checks if the error returned by the state transition matches any expected error. -// A failing expectation returns a wrapped version of the original error, if any, -// or a new error detailing the failing expectation. -// This function does not return or modify the original error, it only evaluates and returns expectations for the error. -func (t *StateTest) checkError(subtest StateSubtest, err error) error { - expectedError := t.json.Post[subtest.Fork][subtest.Index].ExpectException - if err == nil && expectedError == "" { - return nil - } - if err == nil && expectedError != "" { - return fmt.Errorf("expected error %q, got no error", expectedError) - } - if err != nil && expectedError == "" { - return fmt.Errorf("unexpected error: %w", err) - } - if err != nil && expectedError != "" { - // Ignore expected errors (TODO MariusVanDerWijden check error string) - return nil - } - return nil -} - -// Run executes a specific subtest and verifies the post-state and logs -func (t *StateTest) Run(subtest StateSubtest, vmconfig vm.Config, snapshotter bool, scheme string, postCheck func(err error, st *StateTestState)) (result error) { - st, root, err := t.RunNoVerify(subtest, vmconfig, snapshotter, scheme) - // Invoke the callback at the end of function for further analysis. - defer func() { - postCheck(result, &st) - st.Close() - }() - - checkedErr := t.checkError(subtest, err) - if checkedErr != nil { - return checkedErr - } - // The error has been checked; if it was unexpected, it's already returned. - if err != nil { - // Here, an error exists but it was expected. - // We do not check the post state or logs. - return nil - } - post := t.json.Post[subtest.Fork][subtest.Index] - // N.B: We need to do this in a two-step process, because the first Commit takes care - // of self-destructs, and we need to touch the coinbase _after_ it has potentially self-destructed. - if root != common.Hash(post.Root) { - return fmt.Errorf("post state root mismatch: got %x, want %x", root, post.Root) - } - if logs := rlpHash(st.StateDB.Logs()); logs != common.Hash(post.Logs) { - return fmt.Errorf("post state logs hash mismatch: got %x, want %x", logs, post.Logs) - } - st.StateDB, _ = state.New(root, st.StateDB.Database(), st.Snapshots) - return nil -} - -// RunNoVerify runs a specific subtest and returns the statedb and post-state root. -// Remember to call state.Close after verifying the test result! -func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapshotter bool, scheme string) (state StateTestState, root common.Hash, err error) { - config, eips, err := GetChainConfig(subtest.Fork) - if err != nil { - return state, common.Hash{}, UnsupportedForkError{subtest.Fork} - } - vmconfig.ExtraEips = eips - - block := t.genesis(config).ToBlock() - state = MakePreState(rawdb.NewMemoryDatabase(), t.json.Pre, snapshotter, scheme) - - var baseFee *big.Int - if params.GetExtra(config).IsSubnetEVM(0) { - baseFee = t.json.Env.BaseFee - if baseFee == nil { - // Retesteth uses `0x10` for genesis baseFee. Therefore, it defaults to - // parent - 2 : 0xa as the basefee for 'this' context. - baseFee = big.NewInt(0x0a) - } - } - post := t.json.Post[subtest.Fork][subtest.Index] - msg, err := t.json.Tx.toMessage(post, baseFee) - if err != nil { - return state, common.Hash{}, err - } - - { // Blob transactions may be present after the Cancun fork. - // In production, - // - the header is verified against the max in eip4844.go:VerifyEIP4844Header - // - the block body is verified against the header in block_validator.go:ValidateBody - // Here, we just do this shortcut smaller fix, since state tests do not - // utilize those codepaths - if len(msg.BlobHashes)*ethparams.BlobTxBlobGasPerBlob > ethparams.MaxBlobGasPerBlock { - return state, common.Hash{}, errors.New("blob gas exceeds maximum") - } - } - - // Try to recover tx with current signer - if len(post.TxBytes) != 0 { - var ttx types.Transaction - err := ttx.UnmarshalBinary(post.TxBytes) - if err != nil { - return state, common.Hash{}, err - } - if _, err := types.Sender(types.LatestSigner(config), &ttx); err != nil { - return state, common.Hash{}, err - } - } - - // Prepare the EVM. - txContext := core.NewEVMTxContext(msg) - context := core.NewEVMBlockContext(block.Header(), nil, &t.json.Env.Coinbase) - context.GetHash = vmTestBlockHash - context.BaseFee = baseFee - context.Random = nil - if config.IsLondon(new(big.Int)) && t.json.Env.Random != nil { - rnd := common.BigToHash(t.json.Env.Random) - context.Random = &rnd - context.Difficulty = big.NewInt(0) - } - if config.IsCancun(new(big.Int), block.Time()) && t.json.Env.ExcessBlobGas != nil { - context.BlobBaseFee = eip4844.CalcBlobFee(*t.json.Env.ExcessBlobGas) - } - evm := vm.NewEVM(context, txContext, state.StateDB, config, vmconfig) - - // Execute the message. - snapshot := state.StateDB.Snapshot() - gaspool := new(core.GasPool) - gaspool.AddGas(block.GasLimit()) - _, err = core.ApplyMessage(evm, msg, gaspool) - if err != nil { - state.StateDB.RevertToSnapshot(snapshot) - } - // Add 0-value mining reward. This only makes a difference in the cases - // where - // - the coinbase self-destructed, or - // - there are only 'bad' transactions, which aren't executed. In those cases, - // the coinbase gets no txfee, so isn't created, and thus needs to be touched - state.StateDB.AddBalance(block.Coinbase(), new(uint256.Int)) - - // Commit state mutations into database. - root, _ = state.StateDB.Commit(block.NumberU64(), config.IsEIP158(block.Number())) - return state, root, err -} - -func (t *StateTest) genesis(config *params.ChainConfig) *core.Genesis { - genesis := &core.Genesis{ - Config: config, - Coinbase: t.json.Env.Coinbase, - Difficulty: t.json.Env.Difficulty, - GasLimit: t.json.Env.GasLimit, - Number: t.json.Env.Number, - Timestamp: t.json.Env.Timestamp, - Alloc: t.json.Pre, - } - if t.json.Env.Random != nil { - // Post-Merge - genesis.Mixhash = common.BigToHash(t.json.Env.Random) - genesis.Difficulty = big.NewInt(0) - } - return genesis -} - -func (tx *stTransaction) toMessage(ps stPostState, baseFee *big.Int) (*core.Message, error) { - var from common.Address - // If 'sender' field is present, use that - if tx.Sender != nil { - from = *tx.Sender - } else if len(tx.PrivateKey) > 0 { - // Derive sender from private key if needed. - key, err := crypto.ToECDSA(tx.PrivateKey) - if err != nil { - return nil, fmt.Errorf("invalid private key: %v", err) - } - from = crypto.PubkeyToAddress(key.PublicKey) - } - // Parse recipient if present. - var to *common.Address - if tx.To != "" { - to = new(common.Address) - if err := to.UnmarshalText([]byte(tx.To)); err != nil { - return nil, fmt.Errorf("invalid to address: %v", err) - } - } - - // Get values specific to this post state. - if ps.Indexes.Data > len(tx.Data) { - return nil, fmt.Errorf("tx data index %d out of bounds", ps.Indexes.Data) - } - if ps.Indexes.Value > len(tx.Value) { - return nil, fmt.Errorf("tx value index %d out of bounds", ps.Indexes.Value) - } - if ps.Indexes.Gas > len(tx.GasLimit) { - return nil, fmt.Errorf("tx gas limit index %d out of bounds", ps.Indexes.Gas) - } - dataHex := tx.Data[ps.Indexes.Data] - valueHex := tx.Value[ps.Indexes.Value] - gasLimit := tx.GasLimit[ps.Indexes.Gas] - // Value, Data hex encoding is messy: https://github.com/ethereum/tests/issues/203 - value := new(big.Int) - if valueHex != "0x" { - v, ok := math.ParseBig256(valueHex) - if !ok { - return nil, fmt.Errorf("invalid tx value %q", valueHex) - } - value = v - } - data, err := hex.DecodeString(strings.TrimPrefix(dataHex, "0x")) - if err != nil { - return nil, fmt.Errorf("invalid tx data %q", dataHex) - } - var accessList types.AccessList - if tx.AccessLists != nil && tx.AccessLists[ps.Indexes.Data] != nil { - accessList = *tx.AccessLists[ps.Indexes.Data] - } - // If baseFee provided, set gasPrice to effectiveGasPrice. - gasPrice := tx.GasPrice - if baseFee != nil { - if tx.MaxFeePerGas == nil { - tx.MaxFeePerGas = gasPrice - } - if tx.MaxFeePerGas == nil { - tx.MaxFeePerGas = new(big.Int) - } - if tx.MaxPriorityFeePerGas == nil { - tx.MaxPriorityFeePerGas = tx.MaxFeePerGas - } - gasPrice = math.BigMin(new(big.Int).Add(tx.MaxPriorityFeePerGas, baseFee), - tx.MaxFeePerGas) - } - if gasPrice == nil { - return nil, errors.New("no gas price provided") - } - - msg := &core.Message{ - From: from, - To: to, - Nonce: tx.Nonce, - Value: value, - GasLimit: gasLimit, - GasPrice: gasPrice, - GasFeeCap: tx.MaxFeePerGas, - GasTipCap: tx.MaxPriorityFeePerGas, - Data: data, - AccessList: accessList, - BlobHashes: tx.BlobVersionedHashes, - BlobGasFeeCap: tx.BlobGasFeeCap, - } - return msg, nil -} - -func rlpHash(x interface{}) (h common.Hash) { - hw := sha3.NewLegacyKeccak256() - rlp.Encode(hw, x) - hw.Sum(h[:0]) - return h -} - -func vmTestBlockHash(n uint64) common.Hash { - return common.BytesToHash(crypto.Keccak256([]byte(big.NewInt(int64(n)).String()))) -} +// TODO(JonathanOppenheimer): this can all fairly easily be libevm-ified, allowing us to +// delete this file and replace it with the libevm-ified version via import. // StateTestState groups all the state database objects together for use in tests. type StateTestState struct { diff --git a/tests/warp/genesis.json b/tests/warp/genesis.json new file mode 100644 index 0000000000..a72c2f2d54 --- /dev/null +++ b/tests/warp/genesis.json @@ -0,0 +1,46 @@ +{ + "config": { + "chainId": 99999, + "homesteadBlock": 0, + "eip150Block": 0, + "eip155Block": 0, + "eip158Block": 0, + "byzantiumBlock": 0, + "constantinopleBlock": 0, + "petersburgBlock": 0, + "istanbulBlock": 0, + "muirGlacierBlock": 0, + "feeConfig": { + "gasLimit": 20000000, + "minBaseFee": 1000000000, + "targetGas": 100000000, + "baseFeeChangeDenominator": 48, + "minBlockGasCost": 0, + "maxBlockGasCost": 10000000, + "targetBlockRate": 2, + "blockGasCostStep": 500000 + }, + "warpConfig": { + "blockTimestamp": 1607144400 + } + }, + "alloc": { + "8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC": { + "balance": "0x52B7D2DCC80CD2E4000000" + }, + "0x0Fa8EA536Be85F32724D57A37758761B86416123": { + "balance": "0x52B7D2DCC80CD2E4000000" + } + }, + "nonce": "0x0", + "timestamp": "0x5FCB13D0", + "extraData": "0x00", + "gasLimit": "0x1312D00", + "difficulty": "0x0", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "coinbase": "0x0000000000000000000000000000000000000000", + "number": "0x0", + "gasUsed": "0x0", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000" +} + diff --git a/tests/warp/warp_test.go b/tests/warp/warp_test.go index a93909c6e1..2803bc984d 100644 --- a/tests/warp/warp_test.go +++ b/tests/warp/warp_test.go @@ -57,7 +57,11 @@ const ( var ( flagVars *e2e.FlagVars - repoRootPath = tests.GetRepoRootPath("tests/warp") + thisDir, repoRoot = func() (string, string) { + _, thisFile, _, _ := runtime.Caller(0) + dir := filepath.Dir(thisFile) + return dir, filepath.Dir(filepath.Dir(dir)) + }() genesisPath = filepath.Join(repoRootPath, "tests/warp/genesis/genesis.json")