mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-28 07:36:44 +00:00
tests: update tests and add support for general state tests
Tests are now included as a submodule. This should make updating easier and removes ~60MB of JSON data from the working copy. State tests are replaced by General State Tests, which run the same test with multiple fork configurations. With the new test runner, consensus tests are run as subtests by walking json files. Many hex issues have been fixed upstream since the last update and most custom parsing code is replaced by existing JSON hex types. Tests can now be marked as 'expected failures', ensuring that fixes for those tests will trigger an update to test configuration. The new test runner also supports parallel execution and the -short flag.
This commit is contained in:
parent
49a476b219
commit
9af60fa075
24 changed files with 1510 additions and 2679 deletions
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
[submodule "tests"]
|
||||
path = tests/testdata
|
||||
url = https://github.com/ethereum/tests
|
||||
|
|
@ -142,19 +142,19 @@ func TestV3_PBKDF2_1(t *testing.T) {
|
|||
|
||||
func TestV3_PBKDF2_2(t *testing.T) {
|
||||
t.Parallel()
|
||||
tests := loadKeyStoreTestV3("../../tests/files/KeyStoreTests/basic_tests.json", t)
|
||||
tests := loadKeyStoreTestV3("../../tests/testdata/KeyStoreTests/basic_tests.json", t)
|
||||
testDecryptV3(tests["test1"], t)
|
||||
}
|
||||
|
||||
func TestV3_PBKDF2_3(t *testing.T) {
|
||||
t.Parallel()
|
||||
tests := loadKeyStoreTestV3("../../tests/files/KeyStoreTests/basic_tests.json", t)
|
||||
tests := loadKeyStoreTestV3("../../tests/testdata/KeyStoreTests/basic_tests.json", t)
|
||||
testDecryptV3(tests["python_generated_test_with_odd_iv"], t)
|
||||
}
|
||||
|
||||
func TestV3_PBKDF2_4(t *testing.T) {
|
||||
t.Parallel()
|
||||
tests := loadKeyStoreTestV3("../../tests/files/KeyStoreTests/basic_tests.json", t)
|
||||
tests := loadKeyStoreTestV3("../../tests/testdata/KeyStoreTests/basic_tests.json", t)
|
||||
testDecryptV3(tests["evilnonce"], t)
|
||||
}
|
||||
|
||||
|
|
@ -166,7 +166,7 @@ func TestV3_Scrypt_1(t *testing.T) {
|
|||
|
||||
func TestV3_Scrypt_2(t *testing.T) {
|
||||
t.Parallel()
|
||||
tests := loadKeyStoreTestV3("../../tests/files/KeyStoreTests/basic_tests.json", t)
|
||||
tests := loadKeyStoreTestV3("../../tests/testdata/KeyStoreTests/basic_tests.json", t)
|
||||
testDecryptV3(tests["test2"], t)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ var (
|
|||
// paths with any of these prefixes will be skipped
|
||||
skipPrefixes = []string{
|
||||
// boring stuff
|
||||
"vendor/", "tests/files/", "build/",
|
||||
"vendor/", "tests/testdata/", "build/",
|
||||
// don't relicense vendored sources
|
||||
"cmd/internal/browser",
|
||||
"consensus/ethash/xor.go",
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ func (d *diffTest) UnmarshalJSON(b []byte) (err error) {
|
|||
}
|
||||
|
||||
func TestCalcDifficulty(t *testing.T) {
|
||||
file, err := os.Open("../../tests/files/BasicTests/difficulty.json")
|
||||
file, err := os.Open("../../tests/testdata/BasicTests/difficulty.json")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,230 +18,64 @@ package tests
|
|||
|
||||
import (
|
||||
"math/big"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
func TestBcValidBlockTests(t *testing.T) {
|
||||
err := RunBlockTest(big.NewInt(1000000), nil, nil, filepath.Join(blockTestDir, "bcValidBlockTest.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
func TestBlockchain(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
func TestBcUncleHeaderValidityTests(t *testing.T) {
|
||||
err := RunBlockTest(big.NewInt(1000000), nil, nil, filepath.Join(blockTestDir, "bcUncleHeaderValiditiy.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
bt := new(testMatcher)
|
||||
// General state tests are 'exported' as blockchain tests, but we can run them natively.
|
||||
bt.skipLoad(`^GeneralStateTests/`)
|
||||
// Skip random failures due to selfish mining test.
|
||||
bt.skipLoad(`bcForkUncle\.json/ForkUncle`)
|
||||
bt.skipLoad(`^bcMultiChainTest\.json/ChainAtoChainB_blockorder`)
|
||||
bt.skipLoad(`^bcTotalDifficultyTest\.json/(lotsOfLeafs|lotsOfBranches|sideChainWithMoreTransactions)$`)
|
||||
bt.skipLoad(`^bcMultiChainTest\.json/CallContractFromNotBestBlock`)
|
||||
// Expected failures:
|
||||
bt.fails(`(?i)metropolis`, "metropolis is not supported yet")
|
||||
bt.fails(`^TestNetwork/bcTheDaoTest\.json/(DaoTransactions$|DaoTransactions_UncleExtradata$)`, "issue in test")
|
||||
|
||||
func TestBcUncleTests(t *testing.T) {
|
||||
err := RunBlockTest(big.NewInt(1000000), nil, nil, filepath.Join(blockTestDir, "bcUncleTest.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
bt.config(`^TestNetwork/`, params.ChainConfig{
|
||||
HomesteadBlock: big.NewInt(5),
|
||||
DAOForkBlock: big.NewInt(8),
|
||||
DAOForkSupport: true,
|
||||
EIP150Block: big.NewInt(10),
|
||||
EIP155Block: big.NewInt(10),
|
||||
EIP158Block: big.NewInt(14),
|
||||
// MetropolisBlock: big.NewInt(16),
|
||||
})
|
||||
bt.config(`^RandomTests/.*EIP150`, params.ChainConfig{
|
||||
HomesteadBlock: big.NewInt(0),
|
||||
EIP150Block: big.NewInt(0),
|
||||
})
|
||||
bt.config(`^RandomTests/.*EIP158`, params.ChainConfig{
|
||||
HomesteadBlock: big.NewInt(0),
|
||||
EIP150Block: big.NewInt(0),
|
||||
EIP155Block: big.NewInt(0),
|
||||
EIP158Block: big.NewInt(0),
|
||||
})
|
||||
bt.config(`^RandomTests/`, params.ChainConfig{
|
||||
HomesteadBlock: big.NewInt(0),
|
||||
EIP150Block: big.NewInt(10),
|
||||
})
|
||||
bt.config(`^Homestead/`, params.ChainConfig{
|
||||
HomesteadBlock: big.NewInt(0),
|
||||
})
|
||||
bt.config(`^EIP150/`, params.ChainConfig{
|
||||
HomesteadBlock: big.NewInt(0),
|
||||
EIP150Block: big.NewInt(0),
|
||||
})
|
||||
bt.config(`^[^/]+\.json`, params.ChainConfig{
|
||||
HomesteadBlock: big.NewInt(1000000),
|
||||
})
|
||||
|
||||
func TestBcForkUncleTests(t *testing.T) {
|
||||
err := RunBlockTest(big.NewInt(1000000), nil, nil, filepath.Join(blockTestDir, "bcForkUncle.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBcInvalidHeaderTests(t *testing.T) {
|
||||
err := RunBlockTest(big.NewInt(1000000), nil, nil, filepath.Join(blockTestDir, "bcInvalidHeaderTest.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBcInvalidRLPTests(t *testing.T) {
|
||||
err := RunBlockTest(big.NewInt(1000000), nil, nil, filepath.Join(blockTestDir, "bcInvalidRLPTest.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBcRPCAPITests(t *testing.T) {
|
||||
err := RunBlockTest(big.NewInt(1000000), nil, nil, filepath.Join(blockTestDir, "bcRPC_API_Test.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBcForkBlockTests(t *testing.T) {
|
||||
err := RunBlockTest(big.NewInt(1000000), nil, nil, filepath.Join(blockTestDir, "bcForkBlockTest.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBcForkStress(t *testing.T) {
|
||||
err := RunBlockTest(big.NewInt(1000000), nil, nil, filepath.Join(blockTestDir, "bcForkStressTest.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBcTotalDifficulty(t *testing.T) {
|
||||
// skip because these will fail due to selfish mining fix
|
||||
t.Skip()
|
||||
|
||||
err := RunBlockTest(big.NewInt(1000000), nil, nil, filepath.Join(blockTestDir, "bcTotalDifficultyTest.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBcWallet(t *testing.T) {
|
||||
err := RunBlockTest(big.NewInt(1000000), nil, nil, filepath.Join(blockTestDir, "bcWalletTest.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBcGasPricer(t *testing.T) {
|
||||
err := RunBlockTest(big.NewInt(1000000), nil, nil, filepath.Join(blockTestDir, "bcGasPricerTest.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: iterate over files once we got more than a few
|
||||
func TestBcRandom(t *testing.T) {
|
||||
err := RunBlockTest(big.NewInt(1000000), nil, big.NewInt(10), filepath.Join(blockTestDir, "RandomTests/bl201507071825GO.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBcMultiChain(t *testing.T) {
|
||||
// skip due to selfish mining
|
||||
t.Skip()
|
||||
|
||||
err := RunBlockTest(big.NewInt(1000000), nil, big.NewInt(10), filepath.Join(blockTestDir, "bcMultiChainTest.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBcState(t *testing.T) {
|
||||
err := RunBlockTest(big.NewInt(1000000), nil, big.NewInt(10), filepath.Join(blockTestDir, "bcStateTest.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Homestead tests
|
||||
func TestHomesteadBcValidBlockTests(t *testing.T) {
|
||||
err := RunBlockTest(big.NewInt(0), nil, nil, filepath.Join(blockTestDir, "Homestead", "bcValidBlockTest.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHomesteadBcUncleHeaderValidityTests(t *testing.T) {
|
||||
err := RunBlockTest(big.NewInt(0), nil, nil, filepath.Join(blockTestDir, "Homestead", "bcUncleHeaderValiditiy.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHomesteadBcUncleTests(t *testing.T) {
|
||||
err := RunBlockTest(big.NewInt(0), nil, nil, filepath.Join(blockTestDir, "Homestead", "bcUncleTest.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHomesteadBcInvalidHeaderTests(t *testing.T) {
|
||||
err := RunBlockTest(big.NewInt(0), nil, nil, filepath.Join(blockTestDir, "Homestead", "bcInvalidHeaderTest.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHomesteadBcRPCAPITests(t *testing.T) {
|
||||
err := RunBlockTest(big.NewInt(0), nil, nil, filepath.Join(blockTestDir, "Homestead", "bcRPC_API_Test.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHomesteadBcForkStress(t *testing.T) {
|
||||
err := RunBlockTest(big.NewInt(0), nil, nil, filepath.Join(blockTestDir, "Homestead", "bcForkStressTest.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHomesteadBcTotalDifficulty(t *testing.T) {
|
||||
err := RunBlockTest(big.NewInt(0), nil, nil, filepath.Join(blockTestDir, "Homestead", "bcTotalDifficultyTest.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHomesteadBcWallet(t *testing.T) {
|
||||
err := RunBlockTest(big.NewInt(0), nil, nil, filepath.Join(blockTestDir, "Homestead", "bcWalletTest.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHomesteadBcGasPricer(t *testing.T) {
|
||||
err := RunBlockTest(big.NewInt(0), nil, nil, filepath.Join(blockTestDir, "Homestead", "bcGasPricerTest.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHomesteadBcMultiChain(t *testing.T) {
|
||||
err := RunBlockTest(big.NewInt(0), nil, nil, filepath.Join(blockTestDir, "Homestead", "bcMultiChainTest.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHomesteadBcState(t *testing.T) {
|
||||
err := RunBlockTest(big.NewInt(0), nil, nil, filepath.Join(blockTestDir, "Homestead", "bcStateTest.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// DAO hard-fork tests
|
||||
func TestDAOBcTheDao(t *testing.T) {
|
||||
err := RunBlockTest(big.NewInt(5), big.NewInt(8), nil, filepath.Join(blockTestDir, "TestNetwork", "bcTheDaoTest.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEIP150Bc(t *testing.T) {
|
||||
err := RunBlockTest(big.NewInt(0), big.NewInt(8), big.NewInt(10), filepath.Join(blockTestDir, "TestNetwork", "bcEIP150Test.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHomesteadBcExploit(t *testing.T) {
|
||||
err := RunBlockTest(big.NewInt(0), nil, nil, filepath.Join(blockTestDir, "Homestead", "bcExploitTest.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
func TestHomesteadBcShanghaiLove(t *testing.T) {
|
||||
err := RunBlockTest(big.NewInt(0), nil, nil, filepath.Join(blockTestDir, "Homestead", "bcShanghaiLove.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
func TestHomesteadBcSuicideIssue(t *testing.T) {
|
||||
err := RunBlockTest(big.NewInt(0), nil, nil, filepath.Join(blockTestDir, "Homestead", "bcSuicideIssue.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
bt.walk(t, blockTestDir, func(t *testing.T, name string, test *BlockTest) {
|
||||
cfg := bt.findConfig(name)
|
||||
if err := bt.checkFailure(t, name, test.Run(cfg)); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,19 +14,19 @@
|
|||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Package tests implements execution of Ethereum JSON tests.
|
||||
package tests
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
|
|
@ -34,212 +34,115 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
// Block Test JSON Format
|
||||
// A BlockTest checks handling of entire blocks.
|
||||
type BlockTest struct {
|
||||
Genesis *types.Block
|
||||
json btJSON
|
||||
}
|
||||
|
||||
Json *btJSON
|
||||
preAccounts map[string]btAccount
|
||||
postAccounts map[string]btAccount
|
||||
lastblockhash string
|
||||
func (t *BlockTest) UnmarshalJSON(in []byte) error {
|
||||
return json.Unmarshal(in, &t.json)
|
||||
}
|
||||
|
||||
type btJSON struct {
|
||||
Blocks []btBlock
|
||||
GenesisBlockHeader btHeader
|
||||
Pre map[string]btAccount
|
||||
PostState map[string]btAccount
|
||||
Lastblockhash string
|
||||
Blocks []btBlock `json:"blocks"`
|
||||
Genesis btHeader `json:"genesisBlockHeader"`
|
||||
Pre core.GenesisAlloc `json:"pre"`
|
||||
Post core.GenesisAlloc `json:"postState"`
|
||||
BestBlock common.UnprefixedHash `json:"lastblockhash"`
|
||||
}
|
||||
|
||||
type btBlock struct {
|
||||
BlockHeader *btHeader
|
||||
Rlp string
|
||||
Transactions []btTransaction
|
||||
UncleHeaders []*btHeader
|
||||
}
|
||||
|
||||
type btAccount struct {
|
||||
Balance string
|
||||
Code string
|
||||
Nonce string
|
||||
Storage map[string]string
|
||||
PrivateKey string
|
||||
}
|
||||
//go:generate gencodec -type btHeader -field-override btHeaderMarshaling -out gen_btheader.go
|
||||
|
||||
type btHeader struct {
|
||||
Bloom string
|
||||
Coinbase string
|
||||
MixHash string
|
||||
Nonce string
|
||||
Number string
|
||||
Hash string
|
||||
ParentHash string
|
||||
ReceiptTrie string
|
||||
SeedHash string
|
||||
StateRoot string
|
||||
TransactionsTrie string
|
||||
UncleHash string
|
||||
|
||||
ExtraData string
|
||||
Difficulty string
|
||||
GasLimit string
|
||||
GasUsed string
|
||||
Timestamp string
|
||||
Bloom types.Bloom
|
||||
Coinbase common.Address
|
||||
MixHash common.Hash
|
||||
Nonce types.BlockNonce
|
||||
Number *big.Int
|
||||
Hash common.Hash
|
||||
ParentHash common.Hash
|
||||
ReceiptTrie common.Hash
|
||||
StateRoot common.Hash
|
||||
TransactionsTrie common.Hash
|
||||
UncleHash common.Hash
|
||||
ExtraData []byte
|
||||
Difficulty *big.Int
|
||||
GasLimit *big.Int
|
||||
GasUsed *big.Int
|
||||
Timestamp *big.Int
|
||||
}
|
||||
|
||||
type btTransaction struct {
|
||||
Data string
|
||||
GasLimit string
|
||||
GasPrice string
|
||||
Nonce string
|
||||
R string
|
||||
S string
|
||||
To string
|
||||
V string
|
||||
Value string
|
||||
type btHeaderMarshaling struct {
|
||||
ExtraData hexutil.Bytes
|
||||
Number *math.HexOrDecimal256
|
||||
Difficulty *math.HexOrDecimal256
|
||||
GasLimit *math.HexOrDecimal256
|
||||
GasUsed *math.HexOrDecimal256
|
||||
Timestamp *math.HexOrDecimal256
|
||||
}
|
||||
|
||||
func RunBlockTestWithReader(homesteadBlock, daoForkBlock, gasPriceFork *big.Int, r io.Reader, skipTests []string) error {
|
||||
btjs := make(map[string]*btJSON)
|
||||
if err := readJson(r, &btjs); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bt, err := convertBlockTests(btjs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := runBlockTests(homesteadBlock, daoForkBlock, gasPriceFork, bt, skipTests); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func RunBlockTest(homesteadBlock, daoForkBlock, gasPriceFork *big.Int, file string, skipTests []string) error {
|
||||
btjs := make(map[string]*btJSON)
|
||||
if err := readJsonFile(file, &btjs); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bt, err := convertBlockTests(btjs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := runBlockTests(homesteadBlock, daoForkBlock, gasPriceFork, bt, skipTests); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func runBlockTests(homesteadBlock, daoForkBlock, gasPriceFork *big.Int, bt map[string]*BlockTest, skipTests []string) error {
|
||||
skipTest := make(map[string]bool, len(skipTests))
|
||||
for _, name := range skipTests {
|
||||
skipTest[name] = true
|
||||
}
|
||||
|
||||
for name, test := range bt {
|
||||
if skipTest[name] /*|| name != "CallingCanonicalContractFromFork_CALLCODE"*/ {
|
||||
log.Info(fmt.Sprint("Skipping block test", name))
|
||||
continue
|
||||
}
|
||||
// test the block
|
||||
if err := runBlockTest(homesteadBlock, daoForkBlock, gasPriceFork, test); err != nil {
|
||||
return fmt.Errorf("%s: %v", name, err)
|
||||
}
|
||||
log.Info(fmt.Sprint("Block test passed: ", name))
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func runBlockTest(homesteadBlock, daoForkBlock, gasPriceFork *big.Int, test *BlockTest) error {
|
||||
func (t *BlockTest) Run(config *params.ChainConfig) error {
|
||||
// import pre accounts & construct test genesis block & state root
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
if _, err := test.InsertPreState(db); err != nil {
|
||||
return fmt.Errorf("InsertPreState: %v", err)
|
||||
gblock, err := t.genesis(config).Commit(db)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if gblock.Hash() != t.json.Genesis.Hash {
|
||||
return fmt.Errorf("genesis block hash doesn't match test: computed=%x, test=%x\n", gblock.Hash().Bytes()[:6], t.json.Genesis.Hash[:6])
|
||||
}
|
||||
if gblock.Root() != t.json.Genesis.StateRoot {
|
||||
return fmt.Errorf("genesis block state root does not match test: computed=%x, test=%x", gblock.Root().Bytes()[:6], t.json.Genesis.StateRoot[:6])
|
||||
}
|
||||
|
||||
core.WriteTd(db, test.Genesis.Hash(), 0, test.Genesis.Difficulty())
|
||||
core.WriteBlock(db, test.Genesis)
|
||||
core.WriteCanonicalHash(db, test.Genesis.Hash(), test.Genesis.NumberU64())
|
||||
core.WriteHeadBlockHash(db, test.Genesis.Hash())
|
||||
evmux := new(event.TypeMux)
|
||||
config := ¶ms.ChainConfig{HomesteadBlock: homesteadBlock, DAOForkBlock: daoForkBlock, DAOForkSupport: true, EIP150Block: gasPriceFork}
|
||||
chain, err := core.NewBlockChain(db, config, ethash.NewShared(), evmux, vm.Config{})
|
||||
chain, err := core.NewBlockChain(db, config, ethash.NewShared(), new(event.TypeMux), vm.Config{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer chain.Stop()
|
||||
|
||||
//vm.Debug = true
|
||||
validBlocks, err := test.TryBlocksInsert(chain)
|
||||
validBlocks, err := t.insertBlocks(chain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
lastblockhash := common.HexToHash(test.lastblockhash)
|
||||
cmlast := chain.LastBlockHash()
|
||||
if lastblockhash != cmlast {
|
||||
return fmt.Errorf("lastblockhash validation mismatch: want: %x, have: %x", lastblockhash, cmlast)
|
||||
if common.Hash(t.json.BestBlock) != cmlast {
|
||||
return fmt.Errorf("last block hash validation mismatch: want: %x, have: %x", t.json.BestBlock, cmlast)
|
||||
}
|
||||
|
||||
newDB, err := chain.State()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = test.ValidatePostState(newDB); err != nil {
|
||||
if err = t.validatePostState(newDB); err != nil {
|
||||
return fmt.Errorf("post state validation failed: %v", err)
|
||||
}
|
||||
|
||||
return test.ValidateImportedHeaders(chain, validBlocks)
|
||||
return t.validateImportedHeaders(chain, validBlocks)
|
||||
}
|
||||
|
||||
// InsertPreState populates the given database with the genesis
|
||||
// accounts defined by the test.
|
||||
func (t *BlockTest) InsertPreState(db ethdb.Database) (*state.StateDB, error) {
|
||||
statedb, err := state.New(common.Hash{}, state.NewDatabase(db))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
func (t *BlockTest) genesis(config *params.ChainConfig) *core.Genesis {
|
||||
return &core.Genesis{
|
||||
Config: config,
|
||||
Nonce: t.json.Genesis.Nonce.Uint64(),
|
||||
Timestamp: t.json.Genesis.Timestamp.Uint64(),
|
||||
ParentHash: t.json.Genesis.ParentHash,
|
||||
ExtraData: t.json.Genesis.ExtraData,
|
||||
GasLimit: t.json.Genesis.GasLimit.Uint64(),
|
||||
GasUsed: t.json.Genesis.GasUsed.Uint64(),
|
||||
Difficulty: t.json.Genesis.Difficulty,
|
||||
Mixhash: t.json.Genesis.MixHash,
|
||||
Coinbase: t.json.Genesis.Coinbase,
|
||||
Alloc: t.json.Pre,
|
||||
}
|
||||
for addrString, acct := range t.preAccounts {
|
||||
code, err := hex.DecodeString(strings.TrimPrefix(acct.Code, "0x"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
balance, ok := new(big.Int).SetString(acct.Balance, 0)
|
||||
if !ok {
|
||||
return nil, err
|
||||
}
|
||||
nonce, err := strconv.ParseUint(prepInt(16, acct.Nonce), 16, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
addr := common.HexToAddress(addrString)
|
||||
statedb.CreateAccount(addr)
|
||||
statedb.SetCode(addr, code)
|
||||
statedb.SetBalance(addr, balance)
|
||||
statedb.SetNonce(addr, nonce)
|
||||
for k, v := range acct.Storage {
|
||||
statedb.SetState(common.HexToAddress(addrString), common.HexToHash(k), common.HexToHash(v))
|
||||
}
|
||||
}
|
||||
|
||||
root, err := statedb.CommitTo(db, false)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error writing state: %v", err)
|
||||
}
|
||||
if t.Genesis.Root() != root {
|
||||
return nil, fmt.Errorf("computed state root does not match genesis block: genesis=%x computed=%x", t.Genesis.Root().Bytes()[:4], root.Bytes()[:4])
|
||||
}
|
||||
return statedb, nil
|
||||
}
|
||||
|
||||
/* See https://github.com/ethereum/tests/wiki/Blockchain-Tests-II
|
||||
|
|
@ -254,11 +157,11 @@ func (t *BlockTest) InsertPreState(db ethdb.Database) (*state.StateDB, error) {
|
|||
expected we are expected to ignore it and continue processing and then validate the
|
||||
post state.
|
||||
*/
|
||||
func (t *BlockTest) TryBlocksInsert(blockchain *core.BlockChain) ([]btBlock, error) {
|
||||
func (t *BlockTest) insertBlocks(blockchain *core.BlockChain) ([]btBlock, error) {
|
||||
validBlocks := make([]btBlock, 0)
|
||||
// insert the test blocks, which will execute all transactions
|
||||
for _, b := range t.Json.Blocks {
|
||||
cb, err := mustConvertBlock(b)
|
||||
for _, b := range t.json.Blocks {
|
||||
cb, err := b.decode()
|
||||
if err != nil {
|
||||
if b.BlockHeader == nil {
|
||||
continue // OK - block is supposed to be invalid, continue with next block
|
||||
|
|
@ -290,288 +193,99 @@ func (t *BlockTest) TryBlocksInsert(blockchain *core.BlockChain) ([]btBlock, err
|
|||
}
|
||||
|
||||
func validateHeader(h *btHeader, h2 *types.Header) error {
|
||||
expectedBloom := mustConvertBytes(h.Bloom)
|
||||
if !bytes.Equal(expectedBloom, h2.Bloom.Bytes()) {
|
||||
return fmt.Errorf("Bloom: want: %x have: %x", expectedBloom, h2.Bloom.Bytes())
|
||||
if h.Bloom != h2.Bloom {
|
||||
return fmt.Errorf("Bloom: want: %x have: %x", h.Bloom, h2.Bloom)
|
||||
}
|
||||
|
||||
expectedCoinbase := mustConvertBytes(h.Coinbase)
|
||||
if !bytes.Equal(expectedCoinbase, h2.Coinbase.Bytes()) {
|
||||
return fmt.Errorf("Coinbase: want: %x have: %x", expectedCoinbase, h2.Coinbase.Bytes())
|
||||
if h.Coinbase != h2.Coinbase {
|
||||
return fmt.Errorf("Coinbase: want: %x have: %x", h.Coinbase, h2.Coinbase)
|
||||
}
|
||||
|
||||
expectedMixHashBytes := mustConvertBytes(h.MixHash)
|
||||
if !bytes.Equal(expectedMixHashBytes, h2.MixDigest.Bytes()) {
|
||||
return fmt.Errorf("MixHash: want: %x have: %x", expectedMixHashBytes, h2.MixDigest.Bytes())
|
||||
if h.MixHash != h2.MixDigest {
|
||||
return fmt.Errorf("MixHash: want: %x have: %x", h.MixHash, h2.MixDigest)
|
||||
}
|
||||
|
||||
expectedNonce := mustConvertBytes(h.Nonce)
|
||||
if !bytes.Equal(expectedNonce, h2.Nonce[:]) {
|
||||
return fmt.Errorf("Nonce: want: %x have: %x", expectedNonce, h2.Nonce)
|
||||
if h.Nonce != h2.Nonce {
|
||||
return fmt.Errorf("Nonce: want: %x have: %x", h.Nonce, h2.Nonce)
|
||||
}
|
||||
|
||||
expectedNumber := mustConvertBigInt(h.Number, 16)
|
||||
if expectedNumber.Cmp(h2.Number) != 0 {
|
||||
return fmt.Errorf("Number: want: %v have: %v", expectedNumber, h2.Number)
|
||||
if h.Number.Cmp(h2.Number) != 0 {
|
||||
return fmt.Errorf("Number: want: %v have: %v", h.Number, h2.Number)
|
||||
}
|
||||
|
||||
expectedParentHash := mustConvertBytes(h.ParentHash)
|
||||
if !bytes.Equal(expectedParentHash, h2.ParentHash.Bytes()) {
|
||||
return fmt.Errorf("Parent hash: want: %x have: %x", expectedParentHash, h2.ParentHash.Bytes())
|
||||
if h.ParentHash != h2.ParentHash {
|
||||
return fmt.Errorf("Parent hash: want: %x have: %x", h.ParentHash, h2.ParentHash)
|
||||
}
|
||||
|
||||
expectedReceiptHash := mustConvertBytes(h.ReceiptTrie)
|
||||
if !bytes.Equal(expectedReceiptHash, h2.ReceiptHash.Bytes()) {
|
||||
return fmt.Errorf("Receipt hash: want: %x have: %x", expectedReceiptHash, h2.ReceiptHash.Bytes())
|
||||
if h.ReceiptTrie != h2.ReceiptHash {
|
||||
return fmt.Errorf("Receipt hash: want: %x have: %x", h.ReceiptTrie, h2.ReceiptHash)
|
||||
}
|
||||
|
||||
expectedTxHash := mustConvertBytes(h.TransactionsTrie)
|
||||
if !bytes.Equal(expectedTxHash, h2.TxHash.Bytes()) {
|
||||
return fmt.Errorf("Tx hash: want: %x have: %x", expectedTxHash, h2.TxHash.Bytes())
|
||||
if h.TransactionsTrie != h2.TxHash {
|
||||
return fmt.Errorf("Tx hash: want: %x have: %x", h.TransactionsTrie, h2.TxHash)
|
||||
}
|
||||
|
||||
expectedStateHash := mustConvertBytes(h.StateRoot)
|
||||
if !bytes.Equal(expectedStateHash, h2.Root.Bytes()) {
|
||||
return fmt.Errorf("State hash: want: %x have: %x", expectedStateHash, h2.Root.Bytes())
|
||||
if h.StateRoot != h2.Root {
|
||||
return fmt.Errorf("State hash: want: %x have: %x", h.StateRoot, h2.Root)
|
||||
}
|
||||
|
||||
expectedUncleHash := mustConvertBytes(h.UncleHash)
|
||||
if !bytes.Equal(expectedUncleHash, h2.UncleHash.Bytes()) {
|
||||
return fmt.Errorf("Uncle hash: want: %x have: %x", expectedUncleHash, h2.UncleHash.Bytes())
|
||||
if h.UncleHash != h2.UncleHash {
|
||||
return fmt.Errorf("Uncle hash: want: %x have: %x", h.UncleHash, h2.UncleHash)
|
||||
}
|
||||
|
||||
expectedExtraData := mustConvertBytes(h.ExtraData)
|
||||
if !bytes.Equal(expectedExtraData, h2.Extra) {
|
||||
return fmt.Errorf("Extra data: want: %x have: %x", expectedExtraData, h2.Extra)
|
||||
if !bytes.Equal(h.ExtraData, h2.Extra) {
|
||||
return fmt.Errorf("Extra data: want: %x have: %x", h.ExtraData, h2.Extra)
|
||||
}
|
||||
|
||||
expectedDifficulty := mustConvertBigInt(h.Difficulty, 16)
|
||||
if expectedDifficulty.Cmp(h2.Difficulty) != 0 {
|
||||
return fmt.Errorf("Difficulty: want: %v have: %v", expectedDifficulty, h2.Difficulty)
|
||||
if h.Difficulty.Cmp(h2.Difficulty) != 0 {
|
||||
return fmt.Errorf("Difficulty: want: %v have: %v", h.Difficulty, h2.Difficulty)
|
||||
}
|
||||
|
||||
expectedGasLimit := mustConvertBigInt(h.GasLimit, 16)
|
||||
if expectedGasLimit.Cmp(h2.GasLimit) != 0 {
|
||||
return fmt.Errorf("GasLimit: want: %v have: %v", expectedGasLimit, h2.GasLimit)
|
||||
if h.GasLimit.Cmp(h2.GasLimit) != 0 {
|
||||
return fmt.Errorf("GasLimit: want: %v have: %v", h.GasLimit, h2.GasLimit)
|
||||
}
|
||||
expectedGasUsed := mustConvertBigInt(h.GasUsed, 16)
|
||||
if expectedGasUsed.Cmp(h2.GasUsed) != 0 {
|
||||
return fmt.Errorf("GasUsed: want: %v have: %v", expectedGasUsed, h2.GasUsed)
|
||||
if h.GasUsed.Cmp(h2.GasUsed) != 0 {
|
||||
return fmt.Errorf("GasUsed: want: %v have: %v", h.GasUsed, h2.GasUsed)
|
||||
}
|
||||
|
||||
expectedTimestamp := mustConvertBigInt(h.Timestamp, 16)
|
||||
if expectedTimestamp.Cmp(h2.Time) != 0 {
|
||||
return fmt.Errorf("Timestamp: want: %v have: %v", expectedTimestamp, h2.Time)
|
||||
if h.Timestamp.Cmp(h2.Time) != 0 {
|
||||
return fmt.Errorf("Timestamp: want: %v have: %v", h.Timestamp, h2.Time)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *BlockTest) ValidatePostState(statedb *state.StateDB) error {
|
||||
func (t *BlockTest) validatePostState(statedb *state.StateDB) error {
|
||||
// validate post state accounts in test file against what we have in state db
|
||||
for addrString, acct := range t.postAccounts {
|
||||
// XXX: is is worth it checking for errors here?
|
||||
addr, err := hex.DecodeString(addrString)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
code, err := hex.DecodeString(strings.TrimPrefix(acct.Code, "0x"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
balance, ok := new(big.Int).SetString(acct.Balance, 0)
|
||||
if !ok {
|
||||
return err
|
||||
}
|
||||
nonce, err := strconv.ParseUint(prepInt(16, acct.Nonce), 16, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for addr, acct := range t.json.Post {
|
||||
// address is indirectly verified by the other fields, as it's the db key
|
||||
code2 := statedb.GetCode(common.BytesToAddress(addr))
|
||||
balance2 := statedb.GetBalance(common.BytesToAddress(addr))
|
||||
nonce2 := statedb.GetNonce(common.BytesToAddress(addr))
|
||||
if !bytes.Equal(code2, code) {
|
||||
return fmt.Errorf("account code mismatch for addr: %s want: %s have: %s", addrString, hex.EncodeToString(code), hex.EncodeToString(code2))
|
||||
code2 := statedb.GetCode(addr)
|
||||
balance2 := statedb.GetBalance(addr)
|
||||
nonce2 := statedb.GetNonce(addr)
|
||||
if !bytes.Equal(code2, acct.Code) {
|
||||
return fmt.Errorf("account code mismatch for addr: %s want: %v have: %s", addr, acct.Code, hex.EncodeToString(code2))
|
||||
}
|
||||
if balance2.Cmp(balance) != 0 {
|
||||
return fmt.Errorf("account balance mismatch for addr: %s, want: %d, have: %d", addrString, balance, balance2)
|
||||
if balance2.Cmp(acct.Balance) != 0 {
|
||||
return fmt.Errorf("account balance mismatch for addr: %s, want: %d, have: %d", addr, acct.Balance, balance2)
|
||||
}
|
||||
if nonce2 != nonce {
|
||||
return fmt.Errorf("account nonce mismatch for addr: %s want: %d have: %d", addrString, nonce, nonce2)
|
||||
if nonce2 != acct.Nonce {
|
||||
return fmt.Errorf("account nonce mismatch for addr: %s want: %d have: %d", addr, acct.Nonce, nonce2)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (test *BlockTest) ValidateImportedHeaders(cm *core.BlockChain, validBlocks []btBlock) error {
|
||||
func (t *BlockTest) validateImportedHeaders(cm *core.BlockChain, validBlocks []btBlock) error {
|
||||
// to get constant lookup when verifying block headers by hash (some tests have many blocks)
|
||||
bmap := make(map[string]btBlock, len(test.Json.Blocks))
|
||||
bmap := make(map[common.Hash]btBlock, len(t.json.Blocks))
|
||||
for _, b := range validBlocks {
|
||||
bmap[b.BlockHeader.Hash] = b
|
||||
}
|
||||
|
||||
// iterate over blocks backwards from HEAD and validate imported
|
||||
// headers vs test file. some tests have reorgs, and we import
|
||||
// block-by-block, so we can only validate imported headers after
|
||||
// all blocks have been processed by ChainManager, as they may not
|
||||
// all blocks have been processed by BlockChain, as they may not
|
||||
// be part of the longest chain until last block is imported.
|
||||
for b := cm.CurrentBlock(); b != nil && b.NumberU64() != 0; b = cm.GetBlockByHash(b.Header().ParentHash) {
|
||||
bHash := common.Bytes2Hex(b.Hash().Bytes()) // hex without 0x prefix
|
||||
if err := validateHeader(bmap[bHash].BlockHeader, b.Header()); err != nil {
|
||||
if err := validateHeader(bmap[b.Hash()].BlockHeader, b.Header()); err != nil {
|
||||
return fmt.Errorf("Imported block header validation failed: %v", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func convertBlockTests(in map[string]*btJSON) (map[string]*BlockTest, error) {
|
||||
out := make(map[string]*BlockTest)
|
||||
for name, test := range in {
|
||||
var err error
|
||||
if out[name], err = convertBlockTest(test); err != nil {
|
||||
return out, fmt.Errorf("bad test %q: %v", name, err)
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func convertBlockTest(in *btJSON) (out *BlockTest, err error) {
|
||||
// the conversion handles errors by catching panics.
|
||||
// you might consider this ugly, but the alternative (passing errors)
|
||||
// would be much harder to read.
|
||||
defer func() {
|
||||
if recovered := recover(); recovered != nil {
|
||||
buf := make([]byte, 64<<10)
|
||||
buf = buf[:runtime.Stack(buf, false)]
|
||||
err = fmt.Errorf("%v\n%s", recovered, buf)
|
||||
}
|
||||
}()
|
||||
out = &BlockTest{preAccounts: in.Pre, postAccounts: in.PostState, Json: in, lastblockhash: in.Lastblockhash}
|
||||
out.Genesis = mustConvertGenesis(in.GenesisBlockHeader)
|
||||
return out, err
|
||||
}
|
||||
|
||||
func mustConvertGenesis(testGenesis btHeader) *types.Block {
|
||||
hdr := mustConvertHeader(testGenesis)
|
||||
hdr.Number = big.NewInt(0)
|
||||
|
||||
return types.NewBlockWithHeader(hdr)
|
||||
}
|
||||
|
||||
func mustConvertHeader(in btHeader) *types.Header {
|
||||
// hex decode these fields
|
||||
header := &types.Header{
|
||||
//SeedHash: mustConvertBytes(in.SeedHash),
|
||||
MixDigest: mustConvertHash(in.MixHash),
|
||||
Bloom: mustConvertBloom(in.Bloom),
|
||||
ReceiptHash: mustConvertHash(in.ReceiptTrie),
|
||||
TxHash: mustConvertHash(in.TransactionsTrie),
|
||||
Root: mustConvertHash(in.StateRoot),
|
||||
Coinbase: mustConvertAddress(in.Coinbase),
|
||||
UncleHash: mustConvertHash(in.UncleHash),
|
||||
ParentHash: mustConvertHash(in.ParentHash),
|
||||
Extra: mustConvertBytes(in.ExtraData),
|
||||
GasUsed: mustConvertBigInt(in.GasUsed, 16),
|
||||
GasLimit: mustConvertBigInt(in.GasLimit, 16),
|
||||
Difficulty: mustConvertBigInt(in.Difficulty, 16),
|
||||
Time: mustConvertBigInt(in.Timestamp, 16),
|
||||
Nonce: types.EncodeNonce(mustConvertUint(in.Nonce, 16)),
|
||||
}
|
||||
return header
|
||||
}
|
||||
|
||||
func mustConvertBlock(testBlock btBlock) (*types.Block, error) {
|
||||
var b types.Block
|
||||
r := bytes.NewReader(mustConvertBytes(testBlock.Rlp))
|
||||
err := rlp.Decode(r, &b)
|
||||
return &b, err
|
||||
}
|
||||
|
||||
func mustConvertBytes(in string) []byte {
|
||||
if in == "0x" {
|
||||
return []byte{}
|
||||
}
|
||||
h := unfuckFuckedHex(strings.TrimPrefix(in, "0x"))
|
||||
out, err := hex.DecodeString(h)
|
||||
func (bb *btBlock) decode() (*types.Block, error) {
|
||||
data, err := hexutil.Decode(bb.Rlp)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("invalid hex: %q", h))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func mustConvertHash(in string) common.Hash {
|
||||
out, err := hex.DecodeString(strings.TrimPrefix(in, "0x"))
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("invalid hex: %q", in))
|
||||
}
|
||||
return common.BytesToHash(out)
|
||||
}
|
||||
|
||||
func mustConvertAddress(in string) common.Address {
|
||||
out, err := hex.DecodeString(strings.TrimPrefix(in, "0x"))
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("invalid hex: %q", in))
|
||||
}
|
||||
return common.BytesToAddress(out)
|
||||
}
|
||||
|
||||
func mustConvertBloom(in string) types.Bloom {
|
||||
out, err := hex.DecodeString(strings.TrimPrefix(in, "0x"))
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("invalid hex: %q", in))
|
||||
}
|
||||
return types.BytesToBloom(out)
|
||||
}
|
||||
|
||||
func mustConvertBigInt(in string, base int) *big.Int {
|
||||
in = prepInt(base, in)
|
||||
out, ok := new(big.Int).SetString(in, base)
|
||||
if !ok {
|
||||
panic(fmt.Errorf("invalid integer: %q", in))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func mustConvertUint(in string, base int) uint64 {
|
||||
in = prepInt(base, in)
|
||||
out, err := strconv.ParseUint(in, base, 64)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("invalid integer: %q", in))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func LoadBlockTests(file string) (map[string]*BlockTest, error) {
|
||||
btjs := make(map[string]*btJSON)
|
||||
if err := readJsonFile(file, &btjs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return convertBlockTests(btjs)
|
||||
}
|
||||
|
||||
// Nothing to see here, please move along...
|
||||
func prepInt(base int, s string) string {
|
||||
if base == 16 {
|
||||
s = strings.TrimPrefix(s, "0x")
|
||||
if len(s) == 0 {
|
||||
s = "00"
|
||||
}
|
||||
s = nibbleFix(s)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// don't ask
|
||||
func unfuckFuckedHex(almostHex string) string {
|
||||
return nibbleFix(strings.Replace(almostHex, "v", "", -1))
|
||||
}
|
||||
|
||||
func nibbleFix(s string) string {
|
||||
if len(s)%2 != 0 {
|
||||
s = "0" + s
|
||||
}
|
||||
return s
|
||||
var b types.Block
|
||||
err = rlp.DecodeBytes(data, &b)
|
||||
return &b, err
|
||||
}
|
||||
|
|
|
|||
128
tests/gen_btheader.go
Normal file
128
tests/gen_btheader.go
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
|
||||
|
||||
package tests
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
)
|
||||
|
||||
var _ = (*btHeaderMarshaling)(nil)
|
||||
|
||||
func (b btHeader) MarshalJSON() ([]byte, error) {
|
||||
type btHeader struct {
|
||||
Bloom types.Bloom
|
||||
Coinbase common.Address
|
||||
MixHash common.Hash
|
||||
Nonce types.BlockNonce
|
||||
Number *math.HexOrDecimal256
|
||||
Hash common.Hash
|
||||
ParentHash common.Hash
|
||||
ReceiptTrie common.Hash
|
||||
StateRoot common.Hash
|
||||
TransactionsTrie common.Hash
|
||||
UncleHash common.Hash
|
||||
ExtraData hexutil.Bytes
|
||||
Difficulty *math.HexOrDecimal256
|
||||
GasLimit *math.HexOrDecimal256
|
||||
GasUsed *math.HexOrDecimal256
|
||||
Timestamp *math.HexOrDecimal256
|
||||
}
|
||||
var enc btHeader
|
||||
enc.Bloom = b.Bloom
|
||||
enc.Coinbase = b.Coinbase
|
||||
enc.MixHash = b.MixHash
|
||||
enc.Nonce = b.Nonce
|
||||
enc.Number = (*math.HexOrDecimal256)(b.Number)
|
||||
enc.Hash = b.Hash
|
||||
enc.ParentHash = b.ParentHash
|
||||
enc.ReceiptTrie = b.ReceiptTrie
|
||||
enc.StateRoot = b.StateRoot
|
||||
enc.TransactionsTrie = b.TransactionsTrie
|
||||
enc.UncleHash = b.UncleHash
|
||||
enc.ExtraData = b.ExtraData
|
||||
enc.Difficulty = (*math.HexOrDecimal256)(b.Difficulty)
|
||||
enc.GasLimit = (*math.HexOrDecimal256)(b.GasLimit)
|
||||
enc.GasUsed = (*math.HexOrDecimal256)(b.GasUsed)
|
||||
enc.Timestamp = (*math.HexOrDecimal256)(b.Timestamp)
|
||||
return json.Marshal(&enc)
|
||||
}
|
||||
|
||||
func (b *btHeader) UnmarshalJSON(input []byte) error {
|
||||
type btHeader struct {
|
||||
Bloom *types.Bloom
|
||||
Coinbase *common.Address
|
||||
MixHash *common.Hash
|
||||
Nonce *types.BlockNonce
|
||||
Number *math.HexOrDecimal256
|
||||
Hash *common.Hash
|
||||
ParentHash *common.Hash
|
||||
ReceiptTrie *common.Hash
|
||||
StateRoot *common.Hash
|
||||
TransactionsTrie *common.Hash
|
||||
UncleHash *common.Hash
|
||||
ExtraData hexutil.Bytes
|
||||
Difficulty *math.HexOrDecimal256
|
||||
GasLimit *math.HexOrDecimal256
|
||||
GasUsed *math.HexOrDecimal256
|
||||
Timestamp *math.HexOrDecimal256
|
||||
}
|
||||
var dec btHeader
|
||||
if err := json.Unmarshal(input, &dec); err != nil {
|
||||
return err
|
||||
}
|
||||
if dec.Bloom != nil {
|
||||
b.Bloom = *dec.Bloom
|
||||
}
|
||||
if dec.Coinbase != nil {
|
||||
b.Coinbase = *dec.Coinbase
|
||||
}
|
||||
if dec.MixHash != nil {
|
||||
b.MixHash = *dec.MixHash
|
||||
}
|
||||
if dec.Nonce != nil {
|
||||
b.Nonce = *dec.Nonce
|
||||
}
|
||||
if dec.Number != nil {
|
||||
b.Number = (*big.Int)(dec.Number)
|
||||
}
|
||||
if dec.Hash != nil {
|
||||
b.Hash = *dec.Hash
|
||||
}
|
||||
if dec.ParentHash != nil {
|
||||
b.ParentHash = *dec.ParentHash
|
||||
}
|
||||
if dec.ReceiptTrie != nil {
|
||||
b.ReceiptTrie = *dec.ReceiptTrie
|
||||
}
|
||||
if dec.StateRoot != nil {
|
||||
b.StateRoot = *dec.StateRoot
|
||||
}
|
||||
if dec.TransactionsTrie != nil {
|
||||
b.TransactionsTrie = *dec.TransactionsTrie
|
||||
}
|
||||
if dec.UncleHash != nil {
|
||||
b.UncleHash = *dec.UncleHash
|
||||
}
|
||||
if dec.ExtraData != nil {
|
||||
b.ExtraData = dec.ExtraData
|
||||
}
|
||||
if dec.Difficulty != nil {
|
||||
b.Difficulty = (*big.Int)(dec.Difficulty)
|
||||
}
|
||||
if dec.GasLimit != nil {
|
||||
b.GasLimit = (*big.Int)(dec.GasLimit)
|
||||
}
|
||||
if dec.GasUsed != nil {
|
||||
b.GasUsed = (*big.Int)(dec.GasUsed)
|
||||
}
|
||||
if dec.Timestamp != nil {
|
||||
b.Timestamp = (*big.Int)(dec.Timestamp)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
66
tests/gen_stenv.go
Normal file
66
tests/gen_stenv.go
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
|
||||
|
||||
package tests
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
)
|
||||
|
||||
var _ = (*stEnvMarshaling)(nil)
|
||||
|
||||
func (s stEnv) MarshalJSON() ([]byte, error) {
|
||||
type stEnv struct {
|
||||
Coinbase common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"`
|
||||
Difficulty *math.HexOrDecimal256 `json:"currentDifficulty" gencodec:"required"`
|
||||
GasLimit *math.HexOrDecimal256 `json:"currentGasLimit" gencodec:"required"`
|
||||
Number math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"`
|
||||
Timestamp math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"`
|
||||
}
|
||||
var enc stEnv
|
||||
enc.Coinbase = common.UnprefixedAddress(s.Coinbase)
|
||||
enc.Difficulty = (*math.HexOrDecimal256)(s.Difficulty)
|
||||
enc.GasLimit = (*math.HexOrDecimal256)(s.GasLimit)
|
||||
enc.Number = math.HexOrDecimal64(s.Number)
|
||||
enc.Timestamp = math.HexOrDecimal64(s.Timestamp)
|
||||
return json.Marshal(&enc)
|
||||
}
|
||||
|
||||
func (s *stEnv) UnmarshalJSON(input []byte) error {
|
||||
type stEnv struct {
|
||||
Coinbase *common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"`
|
||||
Difficulty *math.HexOrDecimal256 `json:"currentDifficulty" gencodec:"required"`
|
||||
GasLimit *math.HexOrDecimal256 `json:"currentGasLimit" gencodec:"required"`
|
||||
Number *math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"`
|
||||
Timestamp *math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"`
|
||||
}
|
||||
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 {
|
||||
return errors.New("missing required field 'currentDifficulty' for stEnv")
|
||||
}
|
||||
s.Difficulty = (*big.Int)(dec.Difficulty)
|
||||
if dec.GasLimit == nil {
|
||||
return errors.New("missing required field 'currentGasLimit' for stEnv")
|
||||
}
|
||||
s.GasLimit = (*big.Int)(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)
|
||||
return nil
|
||||
}
|
||||
61
tests/gen_stlog.go
Normal file
61
tests/gen_stlog.go
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
|
||||
|
||||
package tests
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
)
|
||||
|
||||
var _ = (*stLogMarshaling)(nil)
|
||||
|
||||
func (s stLog) MarshalJSON() ([]byte, error) {
|
||||
type stLog struct {
|
||||
Address common.UnprefixedAddress `json:"address"`
|
||||
Data hexutil.Bytes `json:"data"`
|
||||
Topics []common.UnprefixedHash `json:"topics"`
|
||||
Bloom string `json:"bloom"`
|
||||
}
|
||||
var enc stLog
|
||||
enc.Address = common.UnprefixedAddress(s.Address)
|
||||
enc.Data = s.Data
|
||||
if s.Topics != nil {
|
||||
enc.Topics = make([]common.UnprefixedHash, len(s.Topics))
|
||||
for k, v := range s.Topics {
|
||||
enc.Topics[k] = common.UnprefixedHash(v)
|
||||
}
|
||||
}
|
||||
enc.Bloom = s.Bloom
|
||||
return json.Marshal(&enc)
|
||||
}
|
||||
|
||||
func (s *stLog) UnmarshalJSON(input []byte) error {
|
||||
type stLog struct {
|
||||
Address *common.UnprefixedAddress `json:"address"`
|
||||
Data hexutil.Bytes `json:"data"`
|
||||
Topics []common.UnprefixedHash `json:"topics"`
|
||||
Bloom *string `json:"bloom"`
|
||||
}
|
||||
var dec stLog
|
||||
if err := json.Unmarshal(input, &dec); err != nil {
|
||||
return err
|
||||
}
|
||||
if dec.Address != nil {
|
||||
s.Address = common.Address(*dec.Address)
|
||||
}
|
||||
if dec.Data != nil {
|
||||
s.Data = dec.Data
|
||||
}
|
||||
if dec.Topics != nil {
|
||||
s.Topics = make([]common.Hash, len(dec.Topics))
|
||||
for k, v := range dec.Topics {
|
||||
s.Topics[k] = common.Hash(v)
|
||||
}
|
||||
}
|
||||
if dec.Bloom != nil {
|
||||
s.Bloom = *dec.Bloom
|
||||
}
|
||||
return nil
|
||||
}
|
||||
80
tests/gen_sttransaction.go
Normal file
80
tests/gen_sttransaction.go
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
|
||||
|
||||
package tests
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
)
|
||||
|
||||
var _ = (*stTransactionMarshaling)(nil)
|
||||
|
||||
func (s stTransaction) MarshalJSON() ([]byte, error) {
|
||||
type stTransaction struct {
|
||||
GasPrice *math.HexOrDecimal256 `json:"gasPrice"`
|
||||
Nonce math.HexOrDecimal64 `json:"nonce"`
|
||||
To string `json:"to"`
|
||||
Data []string `json:"data"`
|
||||
GasLimit []math.HexOrDecimal64 `json:"gasLimit"`
|
||||
Value []string `json:"value"`
|
||||
PrivateKey hexutil.Bytes `json:"secretKey"`
|
||||
}
|
||||
var enc stTransaction
|
||||
enc.GasPrice = (*math.HexOrDecimal256)(s.GasPrice)
|
||||
enc.Nonce = math.HexOrDecimal64(s.Nonce)
|
||||
enc.To = s.To
|
||||
enc.Data = s.Data
|
||||
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
|
||||
return json.Marshal(&enc)
|
||||
}
|
||||
|
||||
func (s *stTransaction) UnmarshalJSON(input []byte) error {
|
||||
type stTransaction struct {
|
||||
GasPrice *math.HexOrDecimal256 `json:"gasPrice"`
|
||||
Nonce *math.HexOrDecimal64 `json:"nonce"`
|
||||
To *string `json:"to"`
|
||||
Data []string `json:"data"`
|
||||
GasLimit []math.HexOrDecimal64 `json:"gasLimit"`
|
||||
Value []string `json:"value"`
|
||||
PrivateKey hexutil.Bytes `json:"secretKey"`
|
||||
}
|
||||
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.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.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
|
||||
}
|
||||
return nil
|
||||
}
|
||||
95
tests/gen_tttransaction.go
Normal file
95
tests/gen_tttransaction.go
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
|
||||
|
||||
package tests
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
)
|
||||
|
||||
var _ = (*ttTransactionMarshaling)(nil)
|
||||
|
||||
func (t ttTransaction) MarshalJSON() ([]byte, error) {
|
||||
type ttTransaction struct {
|
||||
Data hexutil.Bytes `gencodec:"required"`
|
||||
GasLimit *math.HexOrDecimal256 `gencodec:"required"`
|
||||
GasPrice *math.HexOrDecimal256 `gencodec:"required"`
|
||||
Nonce math.HexOrDecimal64 `gencodec:"required"`
|
||||
Value *math.HexOrDecimal256 `gencodec:"required"`
|
||||
R *math.HexOrDecimal256 `gencodec:"required"`
|
||||
S *math.HexOrDecimal256 `gencodec:"required"`
|
||||
V *math.HexOrDecimal256 `gencodec:"required"`
|
||||
To common.Address `gencodec:"required"`
|
||||
}
|
||||
var enc ttTransaction
|
||||
enc.Data = t.Data
|
||||
enc.GasLimit = (*math.HexOrDecimal256)(t.GasLimit)
|
||||
enc.GasPrice = (*math.HexOrDecimal256)(t.GasPrice)
|
||||
enc.Nonce = math.HexOrDecimal64(t.Nonce)
|
||||
enc.Value = (*math.HexOrDecimal256)(t.Value)
|
||||
enc.R = (*math.HexOrDecimal256)(t.R)
|
||||
enc.S = (*math.HexOrDecimal256)(t.S)
|
||||
enc.V = (*math.HexOrDecimal256)(t.V)
|
||||
enc.To = t.To
|
||||
return json.Marshal(&enc)
|
||||
}
|
||||
|
||||
func (t *ttTransaction) UnmarshalJSON(input []byte) error {
|
||||
type ttTransaction struct {
|
||||
Data hexutil.Bytes `gencodec:"required"`
|
||||
GasLimit *math.HexOrDecimal256 `gencodec:"required"`
|
||||
GasPrice *math.HexOrDecimal256 `gencodec:"required"`
|
||||
Nonce *math.HexOrDecimal64 `gencodec:"required"`
|
||||
Value *math.HexOrDecimal256 `gencodec:"required"`
|
||||
R *math.HexOrDecimal256 `gencodec:"required"`
|
||||
S *math.HexOrDecimal256 `gencodec:"required"`
|
||||
V *math.HexOrDecimal256 `gencodec:"required"`
|
||||
To *common.Address `gencodec:"required"`
|
||||
}
|
||||
var dec ttTransaction
|
||||
if err := json.Unmarshal(input, &dec); err != nil {
|
||||
return err
|
||||
}
|
||||
if dec.Data == nil {
|
||||
return errors.New("missing required field 'data' for ttTransaction")
|
||||
}
|
||||
t.Data = dec.Data
|
||||
if dec.GasLimit == nil {
|
||||
return errors.New("missing required field 'gasLimit' for ttTransaction")
|
||||
}
|
||||
t.GasLimit = (*big.Int)(dec.GasLimit)
|
||||
if dec.GasPrice == nil {
|
||||
return errors.New("missing required field 'gasPrice' for ttTransaction")
|
||||
}
|
||||
t.GasPrice = (*big.Int)(dec.GasPrice)
|
||||
if dec.Nonce == nil {
|
||||
return errors.New("missing required field 'nonce' for ttTransaction")
|
||||
}
|
||||
t.Nonce = uint64(*dec.Nonce)
|
||||
if dec.Value == nil {
|
||||
return errors.New("missing required field 'value' for ttTransaction")
|
||||
}
|
||||
t.Value = (*big.Int)(dec.Value)
|
||||
if dec.R == nil {
|
||||
return errors.New("missing required field 'r' for ttTransaction")
|
||||
}
|
||||
t.R = (*big.Int)(dec.R)
|
||||
if dec.S == nil {
|
||||
return errors.New("missing required field 's' for ttTransaction")
|
||||
}
|
||||
t.S = (*big.Int)(dec.S)
|
||||
if dec.V == nil {
|
||||
return errors.New("missing required field 'v' for ttTransaction")
|
||||
}
|
||||
t.V = (*big.Int)(dec.V)
|
||||
if dec.To == nil {
|
||||
return errors.New("missing required field 'to' for ttTransaction")
|
||||
}
|
||||
t.To = *dec.To
|
||||
return nil
|
||||
}
|
||||
88
tests/gen_vmexec.go
Normal file
88
tests/gen_vmexec.go
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
|
||||
|
||||
package tests
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
)
|
||||
|
||||
var _ = (*vmExecMarshaling)(nil)
|
||||
|
||||
func (v vmExec) MarshalJSON() ([]byte, error) {
|
||||
type vmExec struct {
|
||||
Address common.UnprefixedAddress `json:"address" gencodec:"required"`
|
||||
Caller common.UnprefixedAddress `json:"caller" gencodec:"required"`
|
||||
Origin common.UnprefixedAddress `json:"origin" gencodec:"required"`
|
||||
Code hexutil.Bytes `json:"code" gencodec:"required"`
|
||||
Data hexutil.Bytes `json:"data" gencodec:"required"`
|
||||
Value *math.HexOrDecimal256 `json:"value" gencodec:"required"`
|
||||
GasLimit math.HexOrDecimal64 `json:"gas" gencodec:"required"`
|
||||
GasPrice *math.HexOrDecimal256 `json:"gasPrice" gencodec:"required"`
|
||||
}
|
||||
var enc vmExec
|
||||
enc.Address = common.UnprefixedAddress(v.Address)
|
||||
enc.Caller = common.UnprefixedAddress(v.Caller)
|
||||
enc.Origin = common.UnprefixedAddress(v.Origin)
|
||||
enc.Code = v.Code
|
||||
enc.Data = v.Data
|
||||
enc.Value = (*math.HexOrDecimal256)(v.Value)
|
||||
enc.GasLimit = math.HexOrDecimal64(v.GasLimit)
|
||||
enc.GasPrice = (*math.HexOrDecimal256)(v.GasPrice)
|
||||
return json.Marshal(&enc)
|
||||
}
|
||||
|
||||
func (v *vmExec) UnmarshalJSON(input []byte) error {
|
||||
type vmExec struct {
|
||||
Address *common.UnprefixedAddress `json:"address" gencodec:"required"`
|
||||
Caller *common.UnprefixedAddress `json:"caller" gencodec:"required"`
|
||||
Origin *common.UnprefixedAddress `json:"origin" gencodec:"required"`
|
||||
Code hexutil.Bytes `json:"code" gencodec:"required"`
|
||||
Data hexutil.Bytes `json:"data" gencodec:"required"`
|
||||
Value *math.HexOrDecimal256 `json:"value" gencodec:"required"`
|
||||
GasLimit *math.HexOrDecimal64 `json:"gas" gencodec:"required"`
|
||||
GasPrice *math.HexOrDecimal256 `json:"gasPrice" gencodec:"required"`
|
||||
}
|
||||
var dec vmExec
|
||||
if err := json.Unmarshal(input, &dec); err != nil {
|
||||
return err
|
||||
}
|
||||
if dec.Address == nil {
|
||||
return errors.New("missing required field 'address' for vmExec")
|
||||
}
|
||||
v.Address = common.Address(*dec.Address)
|
||||
if dec.Caller == nil {
|
||||
return errors.New("missing required field 'caller' for vmExec")
|
||||
}
|
||||
v.Caller = common.Address(*dec.Caller)
|
||||
if dec.Origin == nil {
|
||||
return errors.New("missing required field 'origin' for vmExec")
|
||||
}
|
||||
v.Origin = common.Address(*dec.Origin)
|
||||
if dec.Code == nil {
|
||||
return errors.New("missing required field 'code' for vmExec")
|
||||
}
|
||||
v.Code = dec.Code
|
||||
if dec.Data == nil {
|
||||
return errors.New("missing required field 'data' for vmExec")
|
||||
}
|
||||
v.Data = dec.Data
|
||||
if dec.Value == nil {
|
||||
return errors.New("missing required field 'value' for vmExec")
|
||||
}
|
||||
v.Value = (*big.Int)(dec.Value)
|
||||
if dec.GasLimit == nil {
|
||||
return errors.New("missing required field 'gas' for vmExec")
|
||||
}
|
||||
v.GasLimit = uint64(*dec.GasLimit)
|
||||
if dec.GasPrice == nil {
|
||||
return errors.New("missing required field 'gasPrice' for vmExec")
|
||||
}
|
||||
v.GasPrice = (*big.Int)(dec.GasPrice)
|
||||
return nil
|
||||
}
|
||||
119
tests/init.go
119
tests/init.go
|
|
@ -1,119 +0,0 @@
|
|||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Package tests implements execution of Ethereum JSON tests.
|
||||
package tests
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
var (
|
||||
baseDir = filepath.Join(".", "files")
|
||||
blockTestDir = filepath.Join(baseDir, "BlockchainTests")
|
||||
stateTestDir = filepath.Join(baseDir, "StateTests")
|
||||
transactionTestDir = filepath.Join(baseDir, "TransactionTests")
|
||||
vmTestDir = filepath.Join(baseDir, "VMTests")
|
||||
rlpTestDir = filepath.Join(baseDir, "RLPTests")
|
||||
|
||||
BlockSkipTests = []string{
|
||||
// These tests are not valid, as they are out of scope for RLP and
|
||||
// the consensus protocol.
|
||||
"BLOCK__RandomByteAtTheEnd",
|
||||
"TRANSCT__RandomByteAtTheEnd",
|
||||
"BLOCK__ZeroByteAtTheEnd",
|
||||
"TRANSCT__ZeroByteAtTheEnd",
|
||||
|
||||
"ChainAtoChainB_blockorder2",
|
||||
"ChainAtoChainB_blockorder1",
|
||||
|
||||
"GasLimitHigherThan2p63m1", // not yet ;)
|
||||
"SuicideIssue", // fails genesis check
|
||||
}
|
||||
|
||||
/* Go client does not support transaction (account) nonces above 2^64. This
|
||||
technically breaks consensus but is regarded as "reasonable
|
||||
engineering constraint" as accounts cannot easily reach such high
|
||||
nonce values in practice
|
||||
*/
|
||||
TransSkipTests = []string{
|
||||
"TransactionWithHihghNonce256",
|
||||
"Vitalik_15",
|
||||
"Vitalik_16",
|
||||
"Vitalik_17",
|
||||
}
|
||||
StateSkipTests = []string{}
|
||||
VmSkipTests = []string{}
|
||||
)
|
||||
|
||||
func readJson(reader io.Reader, value interface{}) error {
|
||||
data, err := ioutil.ReadAll(reader)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading JSON file: %v", err)
|
||||
}
|
||||
if err = json.Unmarshal(data, &value); err != nil {
|
||||
if syntaxerr, ok := err.(*json.SyntaxError); ok {
|
||||
line := findLine(data, syntaxerr.Offset)
|
||||
return fmt.Errorf("JSON syntax error at line %v: %v", line, err)
|
||||
}
|
||||
return fmt.Errorf("JSON unmarshal error: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func readJsonHttp(uri string, value interface{}) error {
|
||||
resp, err := http.Get(uri)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
return readJson(resp.Body, value)
|
||||
}
|
||||
|
||||
func readJsonFile(fn string, value interface{}) error {
|
||||
file, err := os.Open(fn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
err = readJson(file, value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s in file %s", err.Error(), fn)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// findLine returns the line number for the given offset into data.
|
||||
func findLine(data []byte, offset int64) (line int) {
|
||||
line = 1
|
||||
for i, r := range string(data) {
|
||||
if int64(i) >= offset {
|
||||
return
|
||||
}
|
||||
if r == '\n' {
|
||||
line++
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
263
tests/init_test.go
Normal file
263
tests/init_test.go
Normal file
|
|
@ -0,0 +1,263 @@
|
|||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package tests
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
var (
|
||||
baseDir = filepath.Join(".", "testdata")
|
||||
blockTestDir = filepath.Join(baseDir, "BlockchainTests")
|
||||
stateTestDir = filepath.Join(baseDir, "GeneralStateTests")
|
||||
transactionTestDir = filepath.Join(baseDir, "TransactionTests")
|
||||
vmTestDir = filepath.Join(baseDir, "VMTests")
|
||||
rlpTestDir = filepath.Join(baseDir, "RLPTests")
|
||||
)
|
||||
|
||||
func readJson(reader io.Reader, value interface{}) error {
|
||||
data, err := ioutil.ReadAll(reader)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading JSON file: %v", err)
|
||||
}
|
||||
if err = json.Unmarshal(data, &value); err != nil {
|
||||
if syntaxerr, ok := err.(*json.SyntaxError); ok {
|
||||
line := findLine(data, syntaxerr.Offset)
|
||||
return fmt.Errorf("JSON syntax error at line %v: %v", line, err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func readJsonFile(fn string, value interface{}) error {
|
||||
file, err := os.Open(fn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
err = readJson(file, value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s in file %s", err.Error(), fn)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// findLine returns the line number for the given offset into data.
|
||||
func findLine(data []byte, offset int64) (line int) {
|
||||
line = 1
|
||||
for i, r := range string(data) {
|
||||
if int64(i) >= offset {
|
||||
return
|
||||
}
|
||||
if r == '\n' {
|
||||
line++
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// testMatcher controls skipping and chain config assignment to tests.
|
||||
type testMatcher struct {
|
||||
configpat []testConfig
|
||||
failpat []testFailure
|
||||
skiploadpat []*regexp.Regexp
|
||||
skipshortpat []*regexp.Regexp
|
||||
}
|
||||
|
||||
type testConfig struct {
|
||||
p *regexp.Regexp
|
||||
config params.ChainConfig
|
||||
}
|
||||
|
||||
type testFailure struct {
|
||||
p *regexp.Regexp
|
||||
reason string
|
||||
}
|
||||
|
||||
// skipShortMode skips tests matching when the -short flag is used.
|
||||
func (tm *testMatcher) skipShortMode(pattern string) {
|
||||
tm.skipshortpat = append(tm.skipshortpat, regexp.MustCompile(pattern))
|
||||
}
|
||||
|
||||
// skipLoad skips JSON loading of tests matching the pattern.
|
||||
func (tm *testMatcher) skipLoad(pattern string) {
|
||||
tm.skiploadpat = append(tm.skiploadpat, regexp.MustCompile(pattern))
|
||||
}
|
||||
|
||||
// fails adds an expected failure for tests matching the pattern.
|
||||
func (tm *testMatcher) fails(pattern string, reason string) {
|
||||
if reason == "" {
|
||||
panic("empty fail reason")
|
||||
}
|
||||
tm.failpat = append(tm.failpat, testFailure{regexp.MustCompile(pattern), reason})
|
||||
}
|
||||
|
||||
// config defines chain config for tests matching the pattern.
|
||||
func (tm *testMatcher) config(pattern string, cfg params.ChainConfig) {
|
||||
tm.configpat = append(tm.configpat, testConfig{regexp.MustCompile(pattern), cfg})
|
||||
}
|
||||
|
||||
// findSkip matches name against test skip patterns.
|
||||
func (tm *testMatcher) findSkip(name string) (reason string, skipload bool) {
|
||||
if testing.Short() {
|
||||
for _, re := range tm.skipshortpat {
|
||||
if re.MatchString(name) {
|
||||
return "skipped in -short mode", false
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, re := range tm.skiploadpat {
|
||||
if re.MatchString(name) {
|
||||
return "skipped by skipLoad", true
|
||||
}
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
// findConfig returns the chain config matching defined patterns.
|
||||
func (tm *testMatcher) findConfig(name string) *params.ChainConfig {
|
||||
// TODO(fjl): name can be derived from testing.T when min Go version is 1.8
|
||||
for _, m := range tm.configpat {
|
||||
if m.p.MatchString(name) {
|
||||
return &m.config
|
||||
}
|
||||
}
|
||||
return new(params.ChainConfig)
|
||||
}
|
||||
|
||||
// checkFailure checks whether a failure is expected.
|
||||
func (tm *testMatcher) checkFailure(t *testing.T, name string, err error) error {
|
||||
// TODO(fjl): name can be derived from t when min Go version is 1.8
|
||||
failReason := ""
|
||||
for _, m := range tm.failpat {
|
||||
if m.p.MatchString(name) {
|
||||
failReason = m.reason
|
||||
break
|
||||
}
|
||||
}
|
||||
if failReason != "" {
|
||||
t.Logf("expected failure: %s", failReason)
|
||||
if err != nil {
|
||||
t.Logf("error: %v", err)
|
||||
return nil
|
||||
} else {
|
||||
return fmt.Errorf("test succeeded unexpectedly")
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// walk invokes its runTest argument for all subtests in the given directory.
|
||||
//
|
||||
// runTest should be a function of type func(t *testing.T, name string, x <TestType>),
|
||||
// where TestType is the type of the test contained in test files.
|
||||
func (tm *testMatcher) walk(t *testing.T, dir string, runTest interface{}) {
|
||||
// Walk the directory.
|
||||
dirinfo, err := os.Stat(dir)
|
||||
if os.IsNotExist(err) || !dirinfo.IsDir() {
|
||||
fmt.Fprintf(os.Stderr, "can't find test files in %s, did you clone the tests submodule?\n", dir)
|
||||
t.Skip("missing test files")
|
||||
}
|
||||
err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||||
name := filepath.ToSlash(strings.TrimPrefix(path, dir+string(filepath.Separator)))
|
||||
if info.IsDir() {
|
||||
if _, skipload := tm.findSkip(name + "/"); skipload {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if filepath.Ext(path) == ".json" {
|
||||
t.Run(name, func(t *testing.T) { tm.runTestFile(t, path, name, runTest) })
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (tm *testMatcher) runTestFile(t *testing.T, path, name string, runTest interface{}) {
|
||||
if r, _ := tm.findSkip(name); r != "" {
|
||||
t.Skip(r)
|
||||
}
|
||||
t.Parallel()
|
||||
|
||||
// Load the file as map[string]<testType>.
|
||||
m := makeMapFromTestFunc(runTest)
|
||||
if err := readJsonFile(path, m.Addr().Interface()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Run all tests from the map. Don't wrap in a subtest if there is only one test in the file.
|
||||
keys := sortedMapKeys(m)
|
||||
if len(keys) == 1 {
|
||||
runTestFunc(runTest, t, name, m, keys[0])
|
||||
} else {
|
||||
for _, key := range keys {
|
||||
name := name + "/" + key
|
||||
t.Run(key, func(t *testing.T) {
|
||||
if r, _ := tm.findSkip(name); r != "" {
|
||||
t.Skip(r)
|
||||
}
|
||||
runTestFunc(runTest, t, name, m, key)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func makeMapFromTestFunc(f interface{}) reflect.Value {
|
||||
stringT := reflect.TypeOf("")
|
||||
testingT := reflect.TypeOf((*testing.T)(nil))
|
||||
ftyp := reflect.TypeOf(f)
|
||||
if ftyp.Kind() != reflect.Func || ftyp.NumIn() != 3 || ftyp.NumOut() != 0 || ftyp.In(0) != testingT || ftyp.In(1) != stringT {
|
||||
panic(fmt.Sprintf("bad test function type: want func(*testing.T, string, <TestType>), have %s", ftyp))
|
||||
}
|
||||
testType := ftyp.In(2)
|
||||
mp := reflect.New(reflect.MapOf(stringT, testType))
|
||||
return mp.Elem()
|
||||
}
|
||||
|
||||
func sortedMapKeys(m reflect.Value) []string {
|
||||
keys := make([]string, m.Len())
|
||||
for i, k := range m.MapKeys() {
|
||||
keys[i] = k.String()
|
||||
}
|
||||
sort.Strings(keys)
|
||||
return keys
|
||||
}
|
||||
|
||||
func runTestFunc(runTest interface{}, t *testing.T, name string, m reflect.Value, key string) {
|
||||
reflect.ValueOf(runTest).Call([]reflect.Value{
|
||||
reflect.ValueOf(t),
|
||||
reflect.ValueOf(name),
|
||||
m.MapIndex(reflect.ValueOf(key)),
|
||||
})
|
||||
}
|
||||
|
|
@ -17,20 +17,15 @@
|
|||
package tests
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRLP(t *testing.T) {
|
||||
err := RunRLPTest(filepath.Join(rlpTestDir, "rlptest.json"), nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRLP_invalid(t *testing.T) {
|
||||
err := RunRLPTest(filepath.Join(rlpTestDir, "invalidRLPTest.json"), nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Parallel()
|
||||
tm := new(testMatcher)
|
||||
tm.walk(t, rlpTestDir, func(t *testing.T, name string, test *RLPTest) {
|
||||
if err := tm.checkFailure(t, name, test.Run()); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,9 +21,7 @@ import (
|
|||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
|
|
@ -44,33 +42,6 @@ type RLPTest struct {
|
|||
Out string
|
||||
}
|
||||
|
||||
// RunRLPTest runs the tests in the given file, skipping tests by name.
|
||||
func RunRLPTest(file string, skip []string) error {
|
||||
f, err := os.Open(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
return RunRLPTestWithReader(f, skip)
|
||||
}
|
||||
|
||||
// RunRLPTest runs the tests encoded in r, skipping tests by name.
|
||||
func RunRLPTestWithReader(r io.Reader, skip []string) error {
|
||||
var tests map[string]*RLPTest
|
||||
if err := readJson(r, &tests); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, s := range skip {
|
||||
delete(tests, s)
|
||||
}
|
||||
for name, test := range tests {
|
||||
if err := test.Run(); err != nil {
|
||||
return fmt.Errorf("test %q failed: %v", name, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run executes the test.
|
||||
func (t *RLPTest) Run() error {
|
||||
outb, err := hex.DecodeString(t.Out)
|
||||
|
|
|
|||
1014
tests/state_test.go
1014
tests/state_test.go
File diff suppressed because it is too large
Load diff
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2015 The go-ethereum Authors
|
||||
// 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
|
||||
|
|
@ -18,180 +18,296 @@ package tests
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
func RunStateTestWithReader(chainConfig *params.ChainConfig, r io.Reader, skipTests []string) error {
|
||||
tests := make(map[string]VmTest)
|
||||
if err := readJson(r, &tests); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := runStateTests(chainConfig, tests, skipTests); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
// This table defines supported forks and their chain config.
|
||||
var stateTestForks = map[string]*params.ChainConfig{
|
||||
"Frontier": ¶ms.ChainConfig{
|
||||
ChainId: big.NewInt(1),
|
||||
},
|
||||
"Homestead": ¶ms.ChainConfig{
|
||||
HomesteadBlock: big.NewInt(0),
|
||||
ChainId: big.NewInt(1),
|
||||
},
|
||||
"EIP150": ¶ms.ChainConfig{
|
||||
HomesteadBlock: big.NewInt(0),
|
||||
EIP150Block: big.NewInt(0),
|
||||
ChainId: big.NewInt(1),
|
||||
},
|
||||
"EIP158": ¶ms.ChainConfig{
|
||||
HomesteadBlock: big.NewInt(0),
|
||||
EIP150Block: big.NewInt(0),
|
||||
EIP155Block: big.NewInt(0),
|
||||
EIP158Block: big.NewInt(0),
|
||||
ChainId: big.NewInt(1),
|
||||
},
|
||||
"Metropolis": ¶ms.ChainConfig{
|
||||
HomesteadBlock: big.NewInt(0),
|
||||
EIP150Block: big.NewInt(0),
|
||||
EIP155Block: big.NewInt(0),
|
||||
EIP158Block: big.NewInt(0),
|
||||
MetropolisBlock: big.NewInt(0),
|
||||
ChainId: big.NewInt(1),
|
||||
},
|
||||
}
|
||||
|
||||
func RunStateTest(chainConfig *params.ChainConfig, p string, skipTests []string) error {
|
||||
tests := make(map[string]VmTest)
|
||||
if err := readJsonFile(p, &tests); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := runStateTests(chainConfig, tests, skipTests); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
func BenchStateTest(chainConfig *params.ChainConfig, p string, conf bconf, b *testing.B) error {
|
||||
tests := make(map[string]VmTest)
|
||||
if err := readJsonFile(p, &tests); err != nil {
|
||||
return err
|
||||
// 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 core.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 *[]stLog `json:"logs"`
|
||||
Indexes struct {
|
||||
Data int `json:"data"`
|
||||
Gas int `json:"gas"`
|
||||
Value int `json:"value"`
|
||||
}
|
||||
test, ok := tests[conf.name]
|
||||
}
|
||||
|
||||
//go:generate 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:"required"`
|
||||
GasLimit *big.Int `json:"currentGasLimit" gencodec:"required"`
|
||||
Number uint64 `json:"currentNumber" gencodec:"required"`
|
||||
Timestamp uint64 `json:"currentTimestamp" gencodec:"required"`
|
||||
}
|
||||
|
||||
type stEnvMarshaling struct {
|
||||
Coinbase common.UnprefixedAddress
|
||||
Difficulty *math.HexOrDecimal256
|
||||
GasLimit *math.HexOrDecimal256
|
||||
Number math.HexOrDecimal64
|
||||
Timestamp math.HexOrDecimal64
|
||||
}
|
||||
|
||||
//go:generate gencodec -type stTransaction -field-override stTransactionMarshaling -out gen_sttransaction.go
|
||||
|
||||
type stTransaction struct {
|
||||
GasPrice *big.Int `json:"gasPrice"`
|
||||
Nonce uint64 `json:"nonce"`
|
||||
To string `json:"to"`
|
||||
Data []string `json:"data"`
|
||||
GasLimit []uint64 `json:"gasLimit"`
|
||||
Value []string `json:"value"`
|
||||
PrivateKey []byte `json:"secretKey"`
|
||||
}
|
||||
|
||||
type stTransactionMarshaling struct {
|
||||
GasPrice *math.HexOrDecimal256
|
||||
Nonce math.HexOrDecimal64
|
||||
GasLimit []math.HexOrDecimal64
|
||||
PrivateKey hexutil.Bytes
|
||||
}
|
||||
|
||||
//go:generate gencodec -type stLog -field-override stLogMarshaling -out gen_stlog.go
|
||||
|
||||
type stLog struct {
|
||||
Address common.Address `json:"address"`
|
||||
Data []byte `json:"data"`
|
||||
Topics []common.Hash `json:"topics"`
|
||||
Bloom string `json:"bloom"`
|
||||
}
|
||||
|
||||
type stLogMarshaling struct {
|
||||
Address common.UnprefixedAddress
|
||||
Data hexutil.Bytes
|
||||
Topics []common.UnprefixedHash
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// Run executes a specific subtest.
|
||||
func (t *StateTest) Run(subtest StateSubtest, vmconfig vm.Config) error {
|
||||
config, ok := stateTestForks[subtest.Fork]
|
||||
if !ok {
|
||||
return fmt.Errorf("test not found: %s", conf.name)
|
||||
return fmt.Errorf("no config for fork %q", subtest.Fork)
|
||||
}
|
||||
|
||||
// XXX Yeah, yeah...
|
||||
env := make(map[string]string)
|
||||
env["currentCoinbase"] = test.Env.CurrentCoinbase
|
||||
env["currentDifficulty"] = test.Env.CurrentDifficulty
|
||||
env["currentGasLimit"] = test.Env.CurrentGasLimit
|
||||
env["currentNumber"] = test.Env.CurrentNumber
|
||||
env["previousHash"] = test.Env.PreviousHash
|
||||
if n, ok := test.Env.CurrentTimestamp.(float64); ok {
|
||||
env["currentTimestamp"] = strconv.Itoa(int(n))
|
||||
} else {
|
||||
env["currentTimestamp"] = test.Env.CurrentTimestamp.(string)
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
benchStateTest(chainConfig, test, env, b)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func benchStateTest(chainConfig *params.ChainConfig, test VmTest, env map[string]string, b *testing.B) {
|
||||
b.StopTimer()
|
||||
block, _ := t.genesis(config).ToBlock()
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
statedb := makePreState(db, test.Pre)
|
||||
b.StartTimer()
|
||||
statedb := makePreState(db, t.json.Pre)
|
||||
|
||||
RunState(chainConfig, statedb, db, env, test.Exec)
|
||||
}
|
||||
|
||||
func runStateTests(chainConfig *params.ChainConfig, tests map[string]VmTest, skipTests []string) error {
|
||||
skipTest := make(map[string]bool, len(skipTests))
|
||||
for _, name := range skipTests {
|
||||
skipTest[name] = true
|
||||
}
|
||||
|
||||
for name, test := range tests {
|
||||
if skipTest[name] /*|| name != "JUMPDEST_Attack"*/ {
|
||||
log.Info(fmt.Sprint("Skipping state test", name))
|
||||
continue
|
||||
}
|
||||
|
||||
//fmt.Println("StateTest:", name)
|
||||
if err := runStateTest(chainConfig, test); err != nil {
|
||||
return fmt.Errorf("%s: %s\n", name, err.Error())
|
||||
}
|
||||
|
||||
//log.Info(fmt.Sprint("State test passed: ", name))
|
||||
//fmt.Println(string(statedb.Dump()))
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func runStateTest(chainConfig *params.ChainConfig, test VmTest) error {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
statedb := makePreState(db, test.Pre)
|
||||
|
||||
// XXX Yeah, yeah...
|
||||
env := make(map[string]string)
|
||||
env["currentCoinbase"] = test.Env.CurrentCoinbase
|
||||
env["currentDifficulty"] = test.Env.CurrentDifficulty
|
||||
env["currentGasLimit"] = test.Env.CurrentGasLimit
|
||||
env["currentNumber"] = test.Env.CurrentNumber
|
||||
env["previousHash"] = test.Env.PreviousHash
|
||||
if n, ok := test.Env.CurrentTimestamp.(float64); ok {
|
||||
env["currentTimestamp"] = strconv.Itoa(int(n))
|
||||
} else {
|
||||
env["currentTimestamp"] = test.Env.CurrentTimestamp.(string)
|
||||
}
|
||||
|
||||
ret, logs, root, _ := RunState(chainConfig, statedb, db, env, test.Transaction)
|
||||
|
||||
// Return value:
|
||||
var rexp []byte
|
||||
if strings.HasPrefix(test.Out, "#") {
|
||||
n, _ := strconv.Atoi(test.Out[1:])
|
||||
rexp = make([]byte, n)
|
||||
} else {
|
||||
rexp = common.FromHex(test.Out)
|
||||
}
|
||||
if !bytes.Equal(rexp, ret) {
|
||||
return fmt.Errorf("return failed. Expected %x, got %x\n", rexp, ret)
|
||||
}
|
||||
// Post state content:
|
||||
for addr, account := range test.Post {
|
||||
address := common.HexToAddress(addr)
|
||||
if !statedb.Exist(address) {
|
||||
return fmt.Errorf("did not find expected post-state account: %s", addr)
|
||||
}
|
||||
if balance := statedb.GetBalance(address); balance.Cmp(math.MustParseBig256(account.Balance)) != 0 {
|
||||
return fmt.Errorf("(%x) balance failed. Expected: %v have: %v\n", address[:4], math.MustParseBig256(account.Balance), balance)
|
||||
}
|
||||
if nonce := statedb.GetNonce(address); nonce != math.MustParseUint64(account.Nonce) {
|
||||
return fmt.Errorf("(%x) nonce failed. Expected: %v have: %v\n", address[:4], account.Nonce, nonce)
|
||||
}
|
||||
for addr, value := range account.Storage {
|
||||
v := statedb.GetState(address, common.HexToHash(addr))
|
||||
vexp := common.HexToHash(value)
|
||||
if v != vexp {
|
||||
return fmt.Errorf("storage failed:\n%x: %s:\nexpected: %x\nhave: %x\n(%v %v)\n", address[:4], addr, vexp, v, vexp.Big(), v.Big())
|
||||
}
|
||||
}
|
||||
}
|
||||
// Root:
|
||||
if common.HexToHash(test.PostStateRoot) != root {
|
||||
return fmt.Errorf("Post state root error. Expected: %s have: %x", test.PostStateRoot, root)
|
||||
}
|
||||
// Logs:
|
||||
return checkLogs(test.Logs, logs)
|
||||
}
|
||||
|
||||
func RunState(chainConfig *params.ChainConfig, statedb *state.StateDB, db ethdb.Database, env, tx map[string]string) ([]byte, []*types.Log, common.Hash, error) {
|
||||
environment, msg := NewEVMEnvironment(false, chainConfig, statedb, env, tx)
|
||||
gaspool := new(core.GasPool).AddGas(math.MustParseBig256(env["currentGasLimit"]))
|
||||
|
||||
snapshot := statedb.Snapshot()
|
||||
ret, _, err := core.ApplyMessage(environment, msg, gaspool)
|
||||
post := t.json.Post[subtest.Fork][subtest.Index]
|
||||
msg, err := t.json.Tx.toMessage(post)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
context := core.NewEVMContext(msg, block.Header(), nil, &t.json.Env.Coinbase)
|
||||
context.GetHash = vmTestBlockHash
|
||||
evm := vm.NewEVM(context, statedb, config, vmconfig)
|
||||
|
||||
gaspool := new(core.GasPool)
|
||||
gaspool.AddGas(block.GasLimit())
|
||||
snapshot := statedb.Snapshot()
|
||||
if _, _, err := core.ApplyMessage(evm, msg, gaspool); err != nil {
|
||||
statedb.RevertToSnapshot(snapshot)
|
||||
}
|
||||
root, _ := statedb.CommitTo(db, chainConfig.IsEIP158(environment.Context.BlockNumber))
|
||||
return ret, statedb.Logs(), root, err
|
||||
if post.Logs != nil {
|
||||
if err := checkLogs(statedb.Logs(), *post.Logs); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
root, _ := statedb.CommitTo(db, config.IsEIP158(block.Number()))
|
||||
if root != common.Hash(post.Root) {
|
||||
return fmt.Errorf("post state root mismatch: got %x, want %x", root, post.Root)
|
||||
}
|
||||
// TODO(fjl): check return data
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *StateTest) gasLimit(subtest StateSubtest) uint64 {
|
||||
return t.json.Tx.GasLimit[t.json.Post[subtest.Fork][subtest.Index].Indexes.Gas]
|
||||
}
|
||||
|
||||
func makePreState(db ethdb.Database, accounts core.GenesisAlloc) *state.StateDB {
|
||||
sdb := state.NewDatabase(db)
|
||||
statedb, _ := state.New(common.Hash{}, sdb)
|
||||
for addr, a := range accounts {
|
||||
statedb.SetCode(addr, a.Code)
|
||||
statedb.SetNonce(addr, a.Nonce)
|
||||
statedb.SetBalance(addr, a.Balance)
|
||||
for k, v := range a.Storage {
|
||||
statedb.SetState(addr, k, v)
|
||||
}
|
||||
}
|
||||
// Commit and re-open to start with a clean state.
|
||||
root, _ := statedb.CommitTo(db, false)
|
||||
statedb, _ = state.New(root, sdb)
|
||||
return statedb
|
||||
}
|
||||
|
||||
func (t *StateTest) genesis(config *params.ChainConfig) *core.Genesis {
|
||||
return &core.Genesis{
|
||||
Config: config,
|
||||
Coinbase: t.json.Env.Coinbase,
|
||||
Difficulty: t.json.Env.Difficulty,
|
||||
GasLimit: t.json.Env.GasLimit.Uint64(),
|
||||
Number: t.json.Env.Number,
|
||||
Timestamp: t.json.Env.Timestamp,
|
||||
Alloc: t.json.Pre,
|
||||
}
|
||||
}
|
||||
|
||||
func (tx *stTransaction) toMessage(ps stPostState) (core.Message, error) {
|
||||
// Derive sender from private key if present.
|
||||
var from common.Address
|
||||
if len(tx.PrivateKey) > 0 {
|
||||
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)
|
||||
}
|
||||
|
||||
msg := types.NewMessage(from, to, tx.Nonce, value, new(big.Int).SetUint64(gasLimit), tx.GasPrice, data, true)
|
||||
return msg, nil
|
||||
}
|
||||
|
||||
func checkLogs(have []*types.Log, want []stLog) error {
|
||||
if len(have) != len(want) {
|
||||
return fmt.Errorf("logs length mismatch: got %d, want %d", len(have), len(want))
|
||||
}
|
||||
for i := range have {
|
||||
if have[i].Address != want[i].Address {
|
||||
return fmt.Errorf("log address %d: got %x, want %x", i, have[i].Address, want[i].Address)
|
||||
}
|
||||
if !bytes.Equal(have[i].Data, want[i].Data) {
|
||||
return fmt.Errorf("log data %d: got %x, want %x", i, have[i].Data, want[i].Data)
|
||||
}
|
||||
if !reflect.DeepEqual(have[i].Topics, want[i].Topics) {
|
||||
return fmt.Errorf("log topics %d:\ngot %x\nwant %x", i, have[i].Topics, want[i].Topics)
|
||||
}
|
||||
genBloom := math.PaddedBigBytes(types.LogsBloom([]*types.Log{have[i]}), 256)
|
||||
var wantBloom types.Bloom
|
||||
if err := hexutil.UnmarshalFixedUnprefixedText("Bloom", []byte(want[i].Bloom), wantBloom[:]); err != nil {
|
||||
return fmt.Errorf("test log %d has invalid bloom: %v", i, err)
|
||||
}
|
||||
if !bytes.Equal(genBloom, wantBloom[:]) {
|
||||
return fmt.Errorf("bloom mismatch")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
1
tests/testdata
Submodule
1
tests/testdata
Submodule
|
|
@ -0,0 +1 @@
|
|||
Subproject commit f1de8c3b7fa2c2c0aa281b6b3a1ad7010356c5ff
|
||||
|
|
@ -18,109 +18,37 @@ package tests
|
|||
|
||||
import (
|
||||
"math/big"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
func TestTransactions(t *testing.T) {
|
||||
config := ¶ms.ChainConfig{}
|
||||
err := RunTransactionTests(config, filepath.Join(transactionTestDir, "ttTransactionTest.json"), TransSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
func TestTransaction(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
func TestWrongRLPTransactions(t *testing.T) {
|
||||
config := ¶ms.ChainConfig{}
|
||||
err := RunTransactionTests(config, filepath.Join(transactionTestDir, "ttWrongRLPTransaction.json"), TransSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test10MBTransactions(t *testing.T) {
|
||||
config := ¶ms.ChainConfig{}
|
||||
err := RunTransactionTests(config, filepath.Join(transactionTestDir, "tt10mbDataField.json"), TransSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// homestead tests
|
||||
func TestHomesteadTransactions(t *testing.T) {
|
||||
config := ¶ms.ChainConfig{
|
||||
txt := new(testMatcher)
|
||||
txt.config(`^Homestead/`, params.ChainConfig{
|
||||
HomesteadBlock: big.NewInt(0),
|
||||
}
|
||||
err := RunTransactionTests(config, filepath.Join(transactionTestDir, "Homestead", "ttTransactionTest.json"), TransSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHomesteadWrongRLPTransactions(t *testing.T) {
|
||||
config := ¶ms.ChainConfig{
|
||||
HomesteadBlock: big.NewInt(0),
|
||||
}
|
||||
err := RunTransactionTests(config, filepath.Join(transactionTestDir, "Homestead", "ttWrongRLPTransaction.json"), TransSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHomestead10MBTransactions(t *testing.T) {
|
||||
config := ¶ms.ChainConfig{
|
||||
HomesteadBlock: big.NewInt(0),
|
||||
}
|
||||
err := RunTransactionTests(config, filepath.Join(transactionTestDir, "Homestead", "tt10mbDataField.json"), TransSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHomesteadVitalik(t *testing.T) {
|
||||
config := ¶ms.ChainConfig{
|
||||
HomesteadBlock: big.NewInt(0),
|
||||
}
|
||||
err := RunTransactionTests(config, filepath.Join(transactionTestDir, "Homestead", "ttTransactionTestEip155VitaliksTests.json"), TransSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTxEIP155Transaction(t *testing.T) {
|
||||
config := ¶ms.ChainConfig{
|
||||
ChainId: big.NewInt(1),
|
||||
})
|
||||
txt.config(`^EIP155/`, params.ChainConfig{
|
||||
HomesteadBlock: big.NewInt(0),
|
||||
EIP150Block: big.NewInt(0),
|
||||
EIP155Block: big.NewInt(0),
|
||||
}
|
||||
err := RunTransactionTests(config, filepath.Join(transactionTestDir, "EIP155", "ttTransactionTest.json"), TransSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTxEIP155VitaliksTests(t *testing.T) {
|
||||
config := ¶ms.ChainConfig{
|
||||
EIP158Block: big.NewInt(0),
|
||||
ChainId: big.NewInt(1),
|
||||
HomesteadBlock: big.NewInt(0),
|
||||
EIP155Block: big.NewInt(0),
|
||||
}
|
||||
err := RunTransactionTests(config, filepath.Join(transactionTestDir, "EIP155", "ttTransactionTestEip155VitaliksTests.json"), TransSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
txt.config(`^Metropolis/`, params.ChainConfig{
|
||||
HomesteadBlock: big.NewInt(0),
|
||||
EIP150Block: big.NewInt(0),
|
||||
EIP155Block: big.NewInt(0),
|
||||
EIP158Block: big.NewInt(0),
|
||||
MetropolisBlock: big.NewInt(0),
|
||||
})
|
||||
|
||||
func TestTxEIP155VRule(t *testing.T) {
|
||||
config := ¶ms.ChainConfig{
|
||||
ChainId: big.NewInt(1),
|
||||
HomesteadBlock: big.NewInt(0),
|
||||
EIP155Block: big.NewInt(0),
|
||||
}
|
||||
err := RunTransactionTests(config, filepath.Join(transactionTestDir, "EIP155", "ttTransactionTestVRule.json"), TransSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
txt.walk(t, transactionTestDir, func(t *testing.T, name string, test *TransactionTest) {
|
||||
cfg := txt.findConfig(name)
|
||||
if err := txt.checkFailure(t, name, test.Run(cfg)); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,208 +20,114 @@ import (
|
|||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"runtime"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
// Transaction Test JSON Format
|
||||
type TtTransaction struct {
|
||||
Data string
|
||||
GasLimit string
|
||||
GasPrice string
|
||||
Nonce string
|
||||
R string
|
||||
S string
|
||||
To string
|
||||
V string
|
||||
Value string
|
||||
}
|
||||
|
||||
// TransactionTest checks RLP decoding and sender derivation of transactions.
|
||||
type TransactionTest struct {
|
||||
Blocknumber string
|
||||
Rlp string
|
||||
Sender string
|
||||
Transaction TtTransaction
|
||||
json ttJSON
|
||||
}
|
||||
|
||||
func RunTransactionTestsWithReader(config *params.ChainConfig, r io.Reader, skipTests []string) error {
|
||||
skipTest := make(map[string]bool, len(skipTests))
|
||||
for _, name := range skipTests {
|
||||
skipTest[name] = true
|
||||
}
|
||||
|
||||
bt := make(map[string]TransactionTest)
|
||||
if err := readJson(r, &bt); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for name, test := range bt {
|
||||
// if the test should be skipped, return
|
||||
if skipTest[name] {
|
||||
log.Info(fmt.Sprint("Skipping transaction test", name))
|
||||
return nil
|
||||
}
|
||||
// test the block
|
||||
if err := runTransactionTest(config, test); err != nil {
|
||||
return err
|
||||
}
|
||||
log.Info(fmt.Sprint("Transaction test passed: ", name))
|
||||
|
||||
}
|
||||
return nil
|
||||
type ttJSON struct {
|
||||
BlockNumber math.HexOrDecimal64 `json:"blockNumber"`
|
||||
RLP hexutil.Bytes `json:"rlp"`
|
||||
Sender hexutil.Bytes `json:"sender"`
|
||||
Transaction *ttTransaction `json:"transaction"`
|
||||
}
|
||||
|
||||
func RunTransactionTests(config *params.ChainConfig, file string, skipTests []string) error {
|
||||
tests := make(map[string]TransactionTest)
|
||||
if err := readJsonFile(file, &tests); err != nil {
|
||||
return err
|
||||
}
|
||||
//go:generate gencodec -type ttTransaction -field-override ttTransactionMarshaling -out gen_tttransaction.go
|
||||
|
||||
if err := runTransactionTests(config, tests, skipTests); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
type ttTransaction struct {
|
||||
Data []byte `gencodec:"required"`
|
||||
GasLimit *big.Int `gencodec:"required"`
|
||||
GasPrice *big.Int `gencodec:"required"`
|
||||
Nonce uint64 `gencodec:"required"`
|
||||
Value *big.Int `gencodec:"required"`
|
||||
R *big.Int `gencodec:"required"`
|
||||
S *big.Int `gencodec:"required"`
|
||||
V *big.Int `gencodec:"required"`
|
||||
To common.Address `gencodec:"required"`
|
||||
}
|
||||
|
||||
func runTransactionTests(config *params.ChainConfig, tests map[string]TransactionTest, skipTests []string) error {
|
||||
skipTest := make(map[string]bool, len(skipTests))
|
||||
for _, name := range skipTests {
|
||||
skipTest[name] = true
|
||||
}
|
||||
|
||||
for name, test := range tests {
|
||||
// if the test should be skipped, return
|
||||
if skipTest[name] {
|
||||
log.Info(fmt.Sprint("Skipping transaction test", name))
|
||||
return nil
|
||||
}
|
||||
|
||||
// test the block
|
||||
if err := runTransactionTest(config, test); err != nil {
|
||||
return fmt.Errorf("%s: %v", name, err)
|
||||
}
|
||||
log.Info(fmt.Sprint("Transaction test passed: ", name))
|
||||
|
||||
}
|
||||
return nil
|
||||
type ttTransactionMarshaling struct {
|
||||
Data hexutil.Bytes
|
||||
GasLimit *math.HexOrDecimal256
|
||||
GasPrice *math.HexOrDecimal256
|
||||
Nonce math.HexOrDecimal64
|
||||
Value *math.HexOrDecimal256
|
||||
R *math.HexOrDecimal256
|
||||
S *math.HexOrDecimal256
|
||||
V *math.HexOrDecimal256
|
||||
}
|
||||
|
||||
func runTransactionTest(config *params.ChainConfig, txTest TransactionTest) (err error) {
|
||||
func (tt *TransactionTest) Run(config *params.ChainConfig) error {
|
||||
tx := new(types.Transaction)
|
||||
err = rlp.DecodeBytes(mustConvertBytes(txTest.Rlp), tx)
|
||||
|
||||
if err != nil {
|
||||
if txTest.Sender == "" {
|
||||
// RLP decoding failed and this is expected (test OK)
|
||||
if err := rlp.DecodeBytes(tt.json.RLP, tx); err != nil {
|
||||
if tt.json.Transaction == nil {
|
||||
return nil
|
||||
} else {
|
||||
// RLP decoding failed but is expected to succeed (test FAIL)
|
||||
return fmt.Errorf("RLP decoding failed when expected to succeed: %s", err)
|
||||
return fmt.Errorf("RLP decoding failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
validationError := verifyTxFields(config, txTest, tx)
|
||||
if txTest.Sender == "" {
|
||||
if validationError != nil {
|
||||
// RLP decoding works but validation should fail (test OK)
|
||||
return nil
|
||||
} else {
|
||||
// RLP decoding works but validation should fail (test FAIL)
|
||||
// (this should not be possible but added here for completeness)
|
||||
return errors.New("Field validations succeeded but should fail")
|
||||
}
|
||||
}
|
||||
|
||||
if txTest.Sender != "" {
|
||||
if validationError == nil {
|
||||
// RLP decoding works and validations pass (test OK)
|
||||
return nil
|
||||
} else {
|
||||
// RLP decoding works and validations pass (test FAIL)
|
||||
return fmt.Errorf("Field validations failed after RLP decoding: %s", validationError)
|
||||
}
|
||||
}
|
||||
return errors.New("Should not happen: verify RLP decoding and field validation")
|
||||
}
|
||||
|
||||
func verifyTxFields(chainConfig *params.ChainConfig, txTest TransactionTest, decodedTx *types.Transaction) (err error) {
|
||||
defer func() {
|
||||
if recovered := recover(); recovered != nil {
|
||||
buf := make([]byte, 64<<10)
|
||||
buf = buf[:runtime.Stack(buf, false)]
|
||||
err = fmt.Errorf("%v\n%s", recovered, buf)
|
||||
}
|
||||
}()
|
||||
|
||||
var decodedSender common.Address
|
||||
|
||||
signer := types.MakeSigner(chainConfig, math.MustParseBig256(txTest.Blocknumber))
|
||||
decodedSender, err = types.Sender(signer, decodedTx)
|
||||
// Check sender derivation.
|
||||
signer := types.MakeSigner(config, new(big.Int).SetUint64(uint64(tt.json.BlockNumber)))
|
||||
sender, err := types.Sender(signer, tx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
expectedSender := mustConvertAddress(txTest.Sender)
|
||||
if expectedSender != decodedSender {
|
||||
return fmt.Errorf("Sender mismatch: %x %x", expectedSender, decodedSender)
|
||||
if sender != common.BytesToAddress(tt.json.Sender) {
|
||||
return fmt.Errorf("Sender mismatch: got %x, want %x", sender, tt.json.Sender)
|
||||
}
|
||||
|
||||
expectedData := mustConvertBytes(txTest.Transaction.Data)
|
||||
if !bytes.Equal(expectedData, decodedTx.Data()) {
|
||||
return fmt.Errorf("Tx input data mismatch: %#v %#v", expectedData, decodedTx.Data())
|
||||
// Check decoded fields.
|
||||
err = tt.json.Transaction.verify(signer, tx)
|
||||
if tt.json.Sender == nil && err == nil {
|
||||
return errors.New("field validations succeeded but should fail")
|
||||
}
|
||||
|
||||
expectedGasLimit := mustConvertBigInt(txTest.Transaction.GasLimit, 16)
|
||||
if expectedGasLimit.Cmp(decodedTx.Gas()) != 0 {
|
||||
return fmt.Errorf("GasLimit mismatch: %v %v", expectedGasLimit, decodedTx.Gas())
|
||||
if tt.json.Sender != nil && err != nil {
|
||||
return fmt.Errorf("field validations failed after RLP decoding: %s", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tt *ttTransaction) verify(signer types.Signer, tx *types.Transaction) error {
|
||||
if !bytes.Equal(tx.Data(), tt.Data) {
|
||||
return fmt.Errorf("Tx input data mismatch: got %x want %x", tx.Data(), tt.Data)
|
||||
}
|
||||
if tx.Gas().Cmp(tt.GasLimit) != 0 {
|
||||
return fmt.Errorf("GasLimit mismatch: got %v, want %v", tx.Gas(), tt.GasLimit)
|
||||
}
|
||||
if tx.GasPrice().Cmp(tt.GasPrice) != 0 {
|
||||
return fmt.Errorf("GasPrice mismatch: got %v, want %v", tx.GasPrice(), tt.GasPrice)
|
||||
}
|
||||
if tx.Nonce() != tt.Nonce {
|
||||
return fmt.Errorf("Nonce mismatch: got %v, want %v", tx.Nonce(), tt.Nonce)
|
||||
}
|
||||
v, r, s := tx.RawSignatureValues()
|
||||
if r.Cmp(tt.R) != 0 {
|
||||
return fmt.Errorf("R mismatch: got %v, want %v", r, tt.R)
|
||||
}
|
||||
if s.Cmp(tt.S) != 0 {
|
||||
return fmt.Errorf("S mismatch: got %v, want %v", s, tt.S)
|
||||
}
|
||||
if v.Cmp(tt.V) != 0 {
|
||||
return fmt.Errorf("V mismatch: got %v, want %v", v, tt.V)
|
||||
}
|
||||
if tx.To() == nil {
|
||||
if tt.To != (common.Address{}) {
|
||||
return fmt.Errorf("To mismatch when recipient is nil (contract creation): %x", tt.To)
|
||||
}
|
||||
} else if *tx.To() != tt.To {
|
||||
return fmt.Errorf("To mismatch: got %x, want %x", *tx.To(), tt.To)
|
||||
}
|
||||
if tx.Value().Cmp(tt.Value) != 0 {
|
||||
return fmt.Errorf("Value mismatch: got %x, want %x", tx.Value(), tt.Value)
|
||||
}
|
||||
|
||||
expectedGasPrice := mustConvertBigInt(txTest.Transaction.GasPrice, 16)
|
||||
if expectedGasPrice.Cmp(decodedTx.GasPrice()) != 0 {
|
||||
return fmt.Errorf("GasPrice mismatch: %v %v", expectedGasPrice, decodedTx.GasPrice())
|
||||
}
|
||||
|
||||
expectedNonce := mustConvertUint(txTest.Transaction.Nonce, 16)
|
||||
if expectedNonce != decodedTx.Nonce() {
|
||||
return fmt.Errorf("Nonce mismatch: %v %v", expectedNonce, decodedTx.Nonce())
|
||||
}
|
||||
|
||||
v, r, s := decodedTx.RawSignatureValues()
|
||||
expectedR := mustConvertBigInt(txTest.Transaction.R, 16)
|
||||
if r.Cmp(expectedR) != 0 {
|
||||
return fmt.Errorf("R mismatch: %v %v", expectedR, r)
|
||||
}
|
||||
expectedS := mustConvertBigInt(txTest.Transaction.S, 16)
|
||||
if s.Cmp(expectedS) != 0 {
|
||||
return fmt.Errorf("S mismatch: %v %v", expectedS, s)
|
||||
}
|
||||
expectedV := mustConvertBigInt(txTest.Transaction.V, 16)
|
||||
if v.Cmp(expectedV) != 0 {
|
||||
return fmt.Errorf("V mismatch: %v %v", expectedV, v)
|
||||
}
|
||||
|
||||
expectedTo := mustConvertAddress(txTest.Transaction.To)
|
||||
if decodedTx.To() == nil {
|
||||
if expectedTo != common.BytesToAddress([]byte{}) { // "empty" or "zero" address
|
||||
return fmt.Errorf("To mismatch when recipient is nil (contract creation): %v", expectedTo)
|
||||
}
|
||||
} else {
|
||||
if expectedTo != *decodedTx.To() {
|
||||
return fmt.Errorf("To mismatch: %v %v", expectedTo, *decodedTx.To())
|
||||
}
|
||||
}
|
||||
|
||||
expectedValue := mustConvertBigInt(txTest.Transaction.Value, 16)
|
||||
if expectedValue.Cmp(decodedTx.Value()) != 0 {
|
||||
return fmt.Errorf("Value mismatch: %v %v", expectedValue, decodedTx.Value())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
214
tests/util.go
214
tests/util.go
|
|
@ -1,214 +0,0 @@
|
|||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package tests
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"os"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
var (
|
||||
ForceJit bool
|
||||
EnableJit bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
log.Root().SetHandler(log.LvlFilterHandler(log.LvlCrit, log.StreamHandler(os.Stderr, log.TerminalFormat(false))))
|
||||
if os.Getenv("JITVM") == "true" {
|
||||
ForceJit = true
|
||||
EnableJit = true
|
||||
}
|
||||
}
|
||||
|
||||
func checkLogs(tlog []Log, logs []*types.Log) error {
|
||||
if len(tlog) != len(logs) {
|
||||
return fmt.Errorf("log length mismatch. Expected %d, got %d", len(tlog), len(logs))
|
||||
} else {
|
||||
for i, log := range tlog {
|
||||
if common.HexToAddress(log.AddressF) != logs[i].Address {
|
||||
return fmt.Errorf("log address expected %v got %x", log.AddressF, logs[i].Address)
|
||||
}
|
||||
|
||||
if !bytes.Equal(logs[i].Data, common.FromHex(log.DataF)) {
|
||||
return fmt.Errorf("log data expected %v got %x", log.DataF, logs[i].Data)
|
||||
}
|
||||
|
||||
if len(log.TopicsF) != len(logs[i].Topics) {
|
||||
return fmt.Errorf("log topics length expected %d got %d", len(log.TopicsF), logs[i].Topics)
|
||||
} else {
|
||||
for j, topic := range log.TopicsF {
|
||||
if common.HexToHash(topic) != logs[i].Topics[j] {
|
||||
return fmt.Errorf("log topic[%d] expected %v got %x", j, topic, logs[i].Topics[j])
|
||||
}
|
||||
}
|
||||
}
|
||||
genBloom := math.PaddedBigBytes(types.LogsBloom([]*types.Log{logs[i]}), 256)
|
||||
|
||||
if !bytes.Equal(genBloom, common.Hex2Bytes(log.BloomF)) {
|
||||
return fmt.Errorf("bloom mismatch")
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Account struct {
|
||||
Balance string
|
||||
Code string
|
||||
Nonce string
|
||||
Storage map[string]string
|
||||
}
|
||||
|
||||
type Log struct {
|
||||
AddressF string `json:"address"`
|
||||
DataF string `json:"data"`
|
||||
TopicsF []string `json:"topics"`
|
||||
BloomF string `json:"bloom"`
|
||||
}
|
||||
|
||||
func (self Log) Address() []byte { return common.Hex2Bytes(self.AddressF) }
|
||||
func (self Log) Data() []byte { return common.Hex2Bytes(self.DataF) }
|
||||
func (self Log) RlpData() interface{} { return nil }
|
||||
func (self Log) Topics() [][]byte {
|
||||
t := make([][]byte, len(self.TopicsF))
|
||||
for i, topic := range self.TopicsF {
|
||||
t[i] = common.Hex2Bytes(topic)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func makePreState(db ethdb.Database, accounts map[string]Account) *state.StateDB {
|
||||
sdb := state.NewDatabase(db)
|
||||
statedb, _ := state.New(common.Hash{}, sdb)
|
||||
for addr, account := range accounts {
|
||||
insertAccount(statedb, addr, account)
|
||||
}
|
||||
// Commit and re-open to start with a clean state.
|
||||
root, _ := statedb.CommitTo(db, false)
|
||||
statedb, _ = state.New(root, sdb)
|
||||
return statedb
|
||||
}
|
||||
|
||||
func insertAccount(state *state.StateDB, saddr string, account Account) {
|
||||
if common.IsHex(account.Code) {
|
||||
account.Code = account.Code[2:]
|
||||
}
|
||||
addr := common.HexToAddress(saddr)
|
||||
state.SetCode(addr, common.Hex2Bytes(account.Code))
|
||||
state.SetNonce(addr, math.MustParseUint64(account.Nonce))
|
||||
state.SetBalance(addr, math.MustParseBig256(account.Balance))
|
||||
for a, v := range account.Storage {
|
||||
state.SetState(addr, common.HexToHash(a), common.HexToHash(v))
|
||||
}
|
||||
}
|
||||
|
||||
type VmEnv struct {
|
||||
CurrentCoinbase string
|
||||
CurrentDifficulty string
|
||||
CurrentGasLimit string
|
||||
CurrentNumber string
|
||||
CurrentTimestamp interface{}
|
||||
PreviousHash string
|
||||
}
|
||||
|
||||
type VmTest struct {
|
||||
Callcreates interface{}
|
||||
//Env map[string]string
|
||||
Env VmEnv
|
||||
Exec map[string]string
|
||||
Transaction map[string]string
|
||||
Logs []Log
|
||||
Gas string
|
||||
Out string
|
||||
Post map[string]Account
|
||||
Pre map[string]Account
|
||||
PostStateRoot string
|
||||
}
|
||||
|
||||
func NewEVMEnvironment(vmTest bool, chainConfig *params.ChainConfig, statedb *state.StateDB, envValues map[string]string, tx map[string]string) (*vm.EVM, core.Message) {
|
||||
var (
|
||||
data = common.FromHex(tx["data"])
|
||||
gas = math.MustParseBig256(tx["gasLimit"])
|
||||
price = math.MustParseBig256(tx["gasPrice"])
|
||||
value = math.MustParseBig256(tx["value"])
|
||||
nonce = math.MustParseUint64(tx["nonce"])
|
||||
)
|
||||
|
||||
origin := common.HexToAddress(tx["caller"])
|
||||
if len(tx["secretKey"]) > 0 {
|
||||
key, _ := crypto.HexToECDSA(tx["secretKey"])
|
||||
origin = crypto.PubkeyToAddress(key.PublicKey)
|
||||
}
|
||||
|
||||
var to *common.Address
|
||||
if len(tx["to"]) > 2 {
|
||||
t := common.HexToAddress(tx["to"])
|
||||
to = &t
|
||||
}
|
||||
|
||||
msg := types.NewMessage(origin, to, nonce, value, gas, price, data, true)
|
||||
|
||||
initialCall := true
|
||||
canTransfer := func(db vm.StateDB, address common.Address, amount *big.Int) bool {
|
||||
if vmTest {
|
||||
if initialCall {
|
||||
initialCall = false
|
||||
return true
|
||||
}
|
||||
}
|
||||
return core.CanTransfer(db, address, amount)
|
||||
}
|
||||
transfer := func(db vm.StateDB, sender, recipient common.Address, amount *big.Int) {
|
||||
if vmTest {
|
||||
return
|
||||
}
|
||||
core.Transfer(db, sender, recipient, amount)
|
||||
}
|
||||
|
||||
context := vm.Context{
|
||||
CanTransfer: canTransfer,
|
||||
Transfer: transfer,
|
||||
GetHash: func(n uint64) common.Hash {
|
||||
return common.BytesToHash(crypto.Keccak256([]byte(big.NewInt(int64(n)).String())))
|
||||
},
|
||||
|
||||
Origin: origin,
|
||||
Coinbase: common.HexToAddress(envValues["currentCoinbase"]),
|
||||
BlockNumber: math.MustParseBig256(envValues["currentNumber"]),
|
||||
Time: math.MustParseBig256(envValues["currentTimestamp"]),
|
||||
GasLimit: math.MustParseBig256(envValues["currentGasLimit"]),
|
||||
Difficulty: math.MustParseBig256(envValues["currentDifficulty"]),
|
||||
GasPrice: price,
|
||||
}
|
||||
if context.GasPrice == nil {
|
||||
context.GasPrice = new(big.Int)
|
||||
}
|
||||
return vm.NewEVM(context, statedb, chainConfig, vm.Config{NoRecursion: vmTest}), msg
|
||||
}
|
||||
127
tests/vm_test.go
127
tests/vm_test.go
|
|
@ -17,122 +17,21 @@
|
|||
package tests
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
)
|
||||
|
||||
func BenchmarkVmAckermann32Tests(b *testing.B) {
|
||||
fn := filepath.Join(vmTestDir, "vmPerformanceTest.json")
|
||||
if err := BenchVmTest(fn, bconf{"ackermann32", os.Getenv("JITFORCE") == "true", os.Getenv("JITVM") == "true"}, b); err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
}
|
||||
func TestVM(t *testing.T) {
|
||||
t.Parallel()
|
||||
vmt := new(testMatcher)
|
||||
vmt.fails("^vmSystemOperationsTest.json/createNameRegistrator$", "fails without parallel execution")
|
||||
vmt.skipShortMode("^vmPerformanceTest.json")
|
||||
vmt.skipShortMode("^vmInputLimits(Light)?.json")
|
||||
|
||||
func BenchmarkVmFibonacci16Tests(b *testing.B) {
|
||||
fn := filepath.Join(vmTestDir, "vmPerformanceTest.json")
|
||||
if err := BenchVmTest(fn, bconf{"fibonacci16", os.Getenv("JITFORCE") == "true", os.Getenv("JITVM") == "true"}, b); err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
// I've created a new function for each tests so it's easier to identify where the problem lies if any of them fail.
|
||||
func TestVmVMArithmetic(t *testing.T) {
|
||||
fn := filepath.Join(vmTestDir, "vmArithmeticTest.json")
|
||||
if err := RunVmTest(fn, VmSkipTests); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVmBitwiseLogicOperation(t *testing.T) {
|
||||
fn := filepath.Join(vmTestDir, "vmBitwiseLogicOperationTest.json")
|
||||
if err := RunVmTest(fn, VmSkipTests); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVmBlockInfo(t *testing.T) {
|
||||
fn := filepath.Join(vmTestDir, "vmBlockInfoTest.json")
|
||||
if err := RunVmTest(fn, VmSkipTests); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVmEnvironmentalInfo(t *testing.T) {
|
||||
fn := filepath.Join(vmTestDir, "vmEnvironmentalInfoTest.json")
|
||||
if err := RunVmTest(fn, VmSkipTests); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVmFlowOperation(t *testing.T) {
|
||||
fn := filepath.Join(vmTestDir, "vmIOandFlowOperationsTest.json")
|
||||
if err := RunVmTest(fn, VmSkipTests); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVmLogTest(t *testing.T) {
|
||||
fn := filepath.Join(vmTestDir, "vmLogTest.json")
|
||||
if err := RunVmTest(fn, VmSkipTests); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVmPerformance(t *testing.T) {
|
||||
fn := filepath.Join(vmTestDir, "vmPerformanceTest.json")
|
||||
if err := RunVmTest(fn, VmSkipTests); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVmPushDupSwap(t *testing.T) {
|
||||
fn := filepath.Join(vmTestDir, "vmPushDupSwapTest.json")
|
||||
if err := RunVmTest(fn, VmSkipTests); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVmVMSha3(t *testing.T) {
|
||||
fn := filepath.Join(vmTestDir, "vmSha3Test.json")
|
||||
if err := RunVmTest(fn, VmSkipTests); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVm(t *testing.T) {
|
||||
fn := filepath.Join(vmTestDir, "vmtests.json")
|
||||
if err := RunVmTest(fn, VmSkipTests); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVmLog(t *testing.T) {
|
||||
fn := filepath.Join(vmTestDir, "vmLogTest.json")
|
||||
if err := RunVmTest(fn, VmSkipTests); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVmInputLimits(t *testing.T) {
|
||||
fn := filepath.Join(vmTestDir, "vmInputLimits.json")
|
||||
if err := RunVmTest(fn, VmSkipTests); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVmInputLimitsLight(t *testing.T) {
|
||||
fn := filepath.Join(vmTestDir, "vmInputLimitsLight.json")
|
||||
if err := RunVmTest(fn, VmSkipTests); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVmVMRandom(t *testing.T) {
|
||||
fns, _ := filepath.Glob(filepath.Join(baseDir, "RandomTests", "*"))
|
||||
for _, fn := range fns {
|
||||
if err := RunVmTest(fn, VmSkipTests); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
vmt.walk(t, vmTestDir, func(t *testing.T, name string, test *VMTest) {
|
||||
withTrace(t, test.json.Exec.GasLimit, func(vmconfig vm.Config) error {
|
||||
return vmt.checkFailure(t, name, test.Run(vmconfig))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,218 +18,132 @@ package tests
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
func RunVmTestWithReader(r io.Reader, skipTests []string) error {
|
||||
tests := make(map[string]VmTest)
|
||||
err := readJson(r, &tests)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := runVmTests(tests, skipTests); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
// VMTest checks EVM execution without block or transaction context.
|
||||
// See https://github.com/ethereum/tests/wiki/VM-Tests for the test format specification.
|
||||
type VMTest struct {
|
||||
json vmJSON
|
||||
}
|
||||
|
||||
type bconf struct {
|
||||
name string
|
||||
precomp bool
|
||||
jit bool
|
||||
func (t *VMTest) UnmarshalJSON(data []byte) error {
|
||||
return json.Unmarshal(data, &t.json)
|
||||
}
|
||||
|
||||
func BenchVmTest(p string, conf bconf, b *testing.B) error {
|
||||
tests := make(map[string]VmTest)
|
||||
err := readJsonFile(p, &tests)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
test, ok := tests[conf.name]
|
||||
if !ok {
|
||||
return fmt.Errorf("test not found: %s", conf.name)
|
||||
}
|
||||
|
||||
env := make(map[string]string)
|
||||
env["currentCoinbase"] = test.Env.CurrentCoinbase
|
||||
env["currentDifficulty"] = test.Env.CurrentDifficulty
|
||||
env["currentGasLimit"] = test.Env.CurrentGasLimit
|
||||
env["currentNumber"] = test.Env.CurrentNumber
|
||||
env["previousHash"] = test.Env.PreviousHash
|
||||
if n, ok := test.Env.CurrentTimestamp.(float64); ok {
|
||||
env["currentTimestamp"] = strconv.Itoa(int(n))
|
||||
} else {
|
||||
env["currentTimestamp"] = test.Env.CurrentTimestamp.(string)
|
||||
}
|
||||
|
||||
/*
|
||||
if conf.precomp {
|
||||
program := vm.NewProgram(test.code)
|
||||
err := vm.AttachProgram(program)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
benchVmTest(test, env, b)
|
||||
}
|
||||
|
||||
return nil
|
||||
type vmJSON struct {
|
||||
Env stEnv `json:"env"`
|
||||
Exec vmExec `json:"exec"`
|
||||
Logs []stLog `json:"logs"`
|
||||
GasRemaining *math.HexOrDecimal64 `json:"gas"`
|
||||
Out hexutil.Bytes `json:"out"`
|
||||
Pre core.GenesisAlloc `json:"pre"`
|
||||
Post core.GenesisAlloc `json:"post"`
|
||||
PostStateRoot common.Hash `json:"postStateRoot"`
|
||||
}
|
||||
|
||||
func benchVmTest(test VmTest, env map[string]string, b *testing.B) {
|
||||
b.StopTimer()
|
||||
//go:generate gencodec -type vmExec -field-override vmExecMarshaling -out gen_vmexec.go
|
||||
|
||||
type vmExec struct {
|
||||
Address common.Address `json:"address" gencodec:"required"`
|
||||
Caller common.Address `json:"caller" gencodec:"required"`
|
||||
Origin common.Address `json:"origin" gencodec:"required"`
|
||||
Code []byte `json:"code" gencodec:"required"`
|
||||
Data []byte `json:"data" gencodec:"required"`
|
||||
Value *big.Int `json:"value" gencodec:"required"`
|
||||
GasLimit uint64 `json:"gas" gencodec:"required"`
|
||||
GasPrice *big.Int `json:"gasPrice" gencodec:"required"`
|
||||
}
|
||||
|
||||
type vmExecMarshaling struct {
|
||||
Address common.UnprefixedAddress
|
||||
Caller common.UnprefixedAddress
|
||||
Origin common.UnprefixedAddress
|
||||
Code hexutil.Bytes
|
||||
Data hexutil.Bytes
|
||||
Value *math.HexOrDecimal256
|
||||
GasLimit math.HexOrDecimal64
|
||||
GasPrice *math.HexOrDecimal256
|
||||
}
|
||||
|
||||
func (t *VMTest) Run(vmconfig vm.Config) error {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
statedb := makePreState(db, test.Pre)
|
||||
b.StartTimer()
|
||||
statedb := makePreState(db, t.json.Pre)
|
||||
ret, gasRemaining, err := t.exec(statedb, vmconfig)
|
||||
|
||||
RunVm(statedb, env, test.Exec)
|
||||
}
|
||||
|
||||
func RunVmTest(p string, skipTests []string) error {
|
||||
tests := make(map[string]VmTest)
|
||||
err := readJsonFile(p, &tests)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := runVmTests(tests, skipTests); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func runVmTests(tests map[string]VmTest, skipTests []string) error {
|
||||
skipTest := make(map[string]bool, len(skipTests))
|
||||
for _, name := range skipTests {
|
||||
skipTest[name] = true
|
||||
}
|
||||
|
||||
for name, test := range tests {
|
||||
if skipTest[name] /*|| name != "exp0"*/ {
|
||||
log.Info(fmt.Sprint("Skipping VM test", name))
|
||||
continue
|
||||
if t.json.GasRemaining == nil {
|
||||
if err == nil {
|
||||
return fmt.Errorf("gas unspecified (indicating an error), but VM returned no error")
|
||||
}
|
||||
|
||||
if err := runVmTest(test); err != nil {
|
||||
return fmt.Errorf("%s %s", name, err.Error())
|
||||
if gasRemaining > 0 {
|
||||
return fmt.Errorf("gas unspecified (indicating an error), but VM returned gas remaining > 0")
|
||||
}
|
||||
|
||||
log.Info(fmt.Sprint("VM test passed: ", name))
|
||||
//fmt.Println(string(statedb.Dump()))
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func runVmTest(test VmTest) error {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
statedb := makePreState(db, test.Pre)
|
||||
|
||||
// XXX Yeah, yeah...
|
||||
env := make(map[string]string)
|
||||
env["currentCoinbase"] = test.Env.CurrentCoinbase
|
||||
env["currentDifficulty"] = test.Env.CurrentDifficulty
|
||||
env["currentGasLimit"] = test.Env.CurrentGasLimit
|
||||
env["currentNumber"] = test.Env.CurrentNumber
|
||||
env["previousHash"] = test.Env.PreviousHash
|
||||
if n, ok := test.Env.CurrentTimestamp.(float64); ok {
|
||||
env["currentTimestamp"] = strconv.Itoa(int(n))
|
||||
} else {
|
||||
env["currentTimestamp"] = test.Env.CurrentTimestamp.(string)
|
||||
// Test declares gas, expecting outputs to match.
|
||||
if !bytes.Equal(ret, t.json.Out) {
|
||||
return fmt.Errorf("return data mismatch: got %x, want %x", ret, t.json.Out)
|
||||
}
|
||||
|
||||
var (
|
||||
ret []byte
|
||||
gas *big.Int
|
||||
err error
|
||||
logs []*types.Log
|
||||
)
|
||||
|
||||
ret, logs, gas, err = RunVm(statedb, env, test.Exec)
|
||||
|
||||
// Compare expected and actual return
|
||||
rexp := common.FromHex(test.Out)
|
||||
if !bytes.Equal(rexp, ret) {
|
||||
return fmt.Errorf("return failed. Expected %x, got %x\n", rexp, ret)
|
||||
if gasRemaining != uint64(*t.json.GasRemaining) {
|
||||
return fmt.Errorf("remaining gas %v, want %v", gasRemaining, *t.json.GasRemaining)
|
||||
}
|
||||
|
||||
// Check gas usage
|
||||
if len(test.Gas) == 0 && err == nil {
|
||||
return fmt.Errorf("gas unspecified, indicating an error. VM returned (incorrectly) successful")
|
||||
} else {
|
||||
gexp := math.MustParseBig256(test.Gas)
|
||||
if gexp.Cmp(gas) != 0 {
|
||||
return fmt.Errorf("gas failed. Expected %v, got %v\n", gexp, gas)
|
||||
}
|
||||
}
|
||||
|
||||
// check post state
|
||||
for address, account := range test.Post {
|
||||
accountAddr := common.HexToAddress(address)
|
||||
if !statedb.Exist(accountAddr) {
|
||||
continue
|
||||
}
|
||||
for addr, value := range account.Storage {
|
||||
v := statedb.GetState(accountAddr, common.HexToHash(addr))
|
||||
vexp := common.HexToHash(value)
|
||||
if v != vexp {
|
||||
return fmt.Errorf("(%x: %s) storage failed. Expected %x, got %x (%v %v)\n", addr[:4], addr, vexp, v, vexp.Big(), v.Big())
|
||||
for addr, account := range t.json.Post {
|
||||
for k, wantV := range account.Storage {
|
||||
if haveV := statedb.GetState(addr, k); haveV != wantV {
|
||||
return fmt.Errorf("wrong storage value at %x:\n got %x\n want %x", k, haveV, wantV)
|
||||
}
|
||||
}
|
||||
}
|
||||
// if root := statedb.IntermediateRoot(false); root != t.json.PostStateRoot {
|
||||
// return fmt.Errorf("post state root mismatch, got %x, want %x", root, t.json.PostStateRoot)
|
||||
// }
|
||||
return checkLogs(statedb.Logs(), t.json.Logs)
|
||||
}
|
||||
|
||||
// check logs
|
||||
if len(test.Logs) > 0 {
|
||||
lerr := checkLogs(test.Logs, logs)
|
||||
if lerr != nil {
|
||||
return lerr
|
||||
func (t *VMTest) exec(statedb *state.StateDB, vmconfig vm.Config) ([]byte, uint64, error) {
|
||||
evm := t.newEVM(statedb, vmconfig)
|
||||
e := t.json.Exec
|
||||
return evm.Call(vm.AccountRef(e.Caller), e.Address, e.Data, e.GasLimit, e.Value)
|
||||
}
|
||||
|
||||
func (t *VMTest) newEVM(statedb *state.StateDB, vmconfig vm.Config) *vm.EVM {
|
||||
initialCall := true
|
||||
canTransfer := func(db vm.StateDB, address common.Address, amount *big.Int) bool {
|
||||
if initialCall {
|
||||
initialCall = false
|
||||
return true
|
||||
}
|
||||
return core.CanTransfer(db, address, amount)
|
||||
}
|
||||
|
||||
return nil
|
||||
transfer := func(db vm.StateDB, sender, recipient common.Address, amount *big.Int) {}
|
||||
context := vm.Context{
|
||||
CanTransfer: canTransfer,
|
||||
Transfer: transfer,
|
||||
GetHash: vmTestBlockHash,
|
||||
Origin: t.json.Exec.Origin,
|
||||
Coinbase: t.json.Env.Coinbase,
|
||||
BlockNumber: new(big.Int).SetUint64(t.json.Env.Number),
|
||||
Time: new(big.Int).SetUint64(t.json.Env.Timestamp),
|
||||
GasLimit: t.json.Env.GasLimit,
|
||||
Difficulty: t.json.Env.Difficulty,
|
||||
GasPrice: t.json.Exec.GasPrice,
|
||||
}
|
||||
vmconfig.NoRecursion = true
|
||||
return vm.NewEVM(context, statedb, params.MainnetChainConfig, vmconfig)
|
||||
}
|
||||
|
||||
func RunVm(statedb *state.StateDB, env, exec map[string]string) ([]byte, []*types.Log, *big.Int, error) {
|
||||
chainConfig := ¶ms.ChainConfig{
|
||||
HomesteadBlock: params.MainnetChainConfig.HomesteadBlock,
|
||||
DAOForkBlock: params.MainnetChainConfig.DAOForkBlock,
|
||||
DAOForkSupport: true,
|
||||
}
|
||||
var (
|
||||
to = common.HexToAddress(exec["address"])
|
||||
from = common.HexToAddress(exec["caller"])
|
||||
data = common.FromHex(exec["data"])
|
||||
gas = math.MustParseBig256(exec["gas"])
|
||||
value = math.MustParseBig256(exec["value"])
|
||||
)
|
||||
caller := statedb.GetOrNewStateObject(from)
|
||||
vm.PrecompiledContracts = make(map[common.Address]vm.PrecompiledContract)
|
||||
|
||||
environment, _ := NewEVMEnvironment(true, chainConfig, statedb, env, exec)
|
||||
ret, g, err := environment.Call(caller, to, data, gas.Uint64(), value)
|
||||
return ret, statedb.Logs(), new(big.Int).SetUint64(g), err
|
||||
func vmTestBlockHash(n uint64) common.Hash {
|
||||
return common.BytesToHash(crypto.Keccak256([]byte(big.NewInt(int64(n)).String())))
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue