mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-11 22:43:47 +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) {
|
func TestV3_PBKDF2_2(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
tests := loadKeyStoreTestV3("../../tests/files/KeyStoreTests/basic_tests.json", t)
|
tests := loadKeyStoreTestV3("../../tests/testdata/KeyStoreTests/basic_tests.json", t)
|
||||||
testDecryptV3(tests["test1"], t)
|
testDecryptV3(tests["test1"], t)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestV3_PBKDF2_3(t *testing.T) {
|
func TestV3_PBKDF2_3(t *testing.T) {
|
||||||
t.Parallel()
|
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)
|
testDecryptV3(tests["python_generated_test_with_odd_iv"], t)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestV3_PBKDF2_4(t *testing.T) {
|
func TestV3_PBKDF2_4(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
tests := loadKeyStoreTestV3("../../tests/files/KeyStoreTests/basic_tests.json", t)
|
tests := loadKeyStoreTestV3("../../tests/testdata/KeyStoreTests/basic_tests.json", t)
|
||||||
testDecryptV3(tests["evilnonce"], t)
|
testDecryptV3(tests["evilnonce"], t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -166,7 +166,7 @@ func TestV3_Scrypt_1(t *testing.T) {
|
||||||
|
|
||||||
func TestV3_Scrypt_2(t *testing.T) {
|
func TestV3_Scrypt_2(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
tests := loadKeyStoreTestV3("../../tests/files/KeyStoreTests/basic_tests.json", t)
|
tests := loadKeyStoreTestV3("../../tests/testdata/KeyStoreTests/basic_tests.json", t)
|
||||||
testDecryptV3(tests["test2"], t)
|
testDecryptV3(tests["test2"], t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ var (
|
||||||
// paths with any of these prefixes will be skipped
|
// paths with any of these prefixes will be skipped
|
||||||
skipPrefixes = []string{
|
skipPrefixes = []string{
|
||||||
// boring stuff
|
// boring stuff
|
||||||
"vendor/", "tests/files/", "build/",
|
"vendor/", "tests/testdata/", "build/",
|
||||||
// don't relicense vendored sources
|
// don't relicense vendored sources
|
||||||
"cmd/internal/browser",
|
"cmd/internal/browser",
|
||||||
"consensus/ethash/xor.go",
|
"consensus/ethash/xor.go",
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ func (d *diffTest) UnmarshalJSON(b []byte) (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCalcDifficulty(t *testing.T) {
|
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 {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,230 +18,64 @@ package tests
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math/big"
|
"math/big"
|
||||||
"path/filepath"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestBcValidBlockTests(t *testing.T) {
|
func TestBlockchain(t *testing.T) {
|
||||||
err := RunBlockTest(big.NewInt(1000000), nil, nil, filepath.Join(blockTestDir, "bcValidBlockTest.json"), BlockSkipTests)
|
t.Parallel()
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBcUncleHeaderValidityTests(t *testing.T) {
|
bt := new(testMatcher)
|
||||||
err := RunBlockTest(big.NewInt(1000000), nil, nil, filepath.Join(blockTestDir, "bcUncleHeaderValiditiy.json"), BlockSkipTests)
|
// General state tests are 'exported' as blockchain tests, but we can run them natively.
|
||||||
if err != nil {
|
bt.skipLoad(`^GeneralStateTests/`)
|
||||||
t.Fatal(err)
|
// 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) {
|
bt.config(`^TestNetwork/`, params.ChainConfig{
|
||||||
err := RunBlockTest(big.NewInt(1000000), nil, nil, filepath.Join(blockTestDir, "bcUncleTest.json"), BlockSkipTests)
|
HomesteadBlock: big.NewInt(5),
|
||||||
if err != nil {
|
DAOForkBlock: big.NewInt(8),
|
||||||
t.Fatal(err)
|
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) {
|
bt.walk(t, blockTestDir, func(t *testing.T, name string, test *BlockTest) {
|
||||||
err := RunBlockTest(big.NewInt(1000000), nil, nil, filepath.Join(blockTestDir, "bcForkUncle.json"), BlockSkipTests)
|
cfg := bt.findConfig(name)
|
||||||
if err != nil {
|
if err := bt.checkFailure(t, name, test.Run(cfg)); err != nil {
|
||||||
t.Fatal(err)
|
t.Error(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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,19 +14,19 @@
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
// 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/>.
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
// Package tests implements execution of Ethereum JSON tests.
|
||||||
package tests
|
package tests
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
"runtime"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"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/consensus/ethash"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
|
|
@ -34,212 +34,115 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Block Test JSON Format
|
// A BlockTest checks handling of entire blocks.
|
||||||
type BlockTest struct {
|
type BlockTest struct {
|
||||||
Genesis *types.Block
|
json btJSON
|
||||||
|
}
|
||||||
|
|
||||||
Json *btJSON
|
func (t *BlockTest) UnmarshalJSON(in []byte) error {
|
||||||
preAccounts map[string]btAccount
|
return json.Unmarshal(in, &t.json)
|
||||||
postAccounts map[string]btAccount
|
|
||||||
lastblockhash string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type btJSON struct {
|
type btJSON struct {
|
||||||
Blocks []btBlock
|
Blocks []btBlock `json:"blocks"`
|
||||||
GenesisBlockHeader btHeader
|
Genesis btHeader `json:"genesisBlockHeader"`
|
||||||
Pre map[string]btAccount
|
Pre core.GenesisAlloc `json:"pre"`
|
||||||
PostState map[string]btAccount
|
Post core.GenesisAlloc `json:"postState"`
|
||||||
Lastblockhash string
|
BestBlock common.UnprefixedHash `json:"lastblockhash"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type btBlock struct {
|
type btBlock struct {
|
||||||
BlockHeader *btHeader
|
BlockHeader *btHeader
|
||||||
Rlp string
|
Rlp string
|
||||||
Transactions []btTransaction
|
|
||||||
UncleHeaders []*btHeader
|
UncleHeaders []*btHeader
|
||||||
}
|
}
|
||||||
|
|
||||||
type btAccount struct {
|
//go:generate gencodec -type btHeader -field-override btHeaderMarshaling -out gen_btheader.go
|
||||||
Balance string
|
|
||||||
Code string
|
|
||||||
Nonce string
|
|
||||||
Storage map[string]string
|
|
||||||
PrivateKey string
|
|
||||||
}
|
|
||||||
|
|
||||||
type btHeader struct {
|
type btHeader struct {
|
||||||
Bloom string
|
Bloom types.Bloom
|
||||||
Coinbase string
|
Coinbase common.Address
|
||||||
MixHash string
|
MixHash common.Hash
|
||||||
Nonce string
|
Nonce types.BlockNonce
|
||||||
Number string
|
Number *big.Int
|
||||||
Hash string
|
Hash common.Hash
|
||||||
ParentHash string
|
ParentHash common.Hash
|
||||||
ReceiptTrie string
|
ReceiptTrie common.Hash
|
||||||
SeedHash string
|
StateRoot common.Hash
|
||||||
StateRoot string
|
TransactionsTrie common.Hash
|
||||||
TransactionsTrie string
|
UncleHash common.Hash
|
||||||
UncleHash string
|
ExtraData []byte
|
||||||
|
Difficulty *big.Int
|
||||||
ExtraData string
|
GasLimit *big.Int
|
||||||
Difficulty string
|
GasUsed *big.Int
|
||||||
GasLimit string
|
Timestamp *big.Int
|
||||||
GasUsed string
|
|
||||||
Timestamp string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type btTransaction struct {
|
type btHeaderMarshaling struct {
|
||||||
Data string
|
ExtraData hexutil.Bytes
|
||||||
GasLimit string
|
Number *math.HexOrDecimal256
|
||||||
GasPrice string
|
Difficulty *math.HexOrDecimal256
|
||||||
Nonce string
|
GasLimit *math.HexOrDecimal256
|
||||||
R string
|
GasUsed *math.HexOrDecimal256
|
||||||
S string
|
Timestamp *math.HexOrDecimal256
|
||||||
To string
|
|
||||||
V string
|
|
||||||
Value string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func RunBlockTestWithReader(homesteadBlock, daoForkBlock, gasPriceFork *big.Int, r io.Reader, skipTests []string) error {
|
func (t *BlockTest) Run(config *params.ChainConfig) 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 {
|
|
||||||
// import pre accounts & construct test genesis block & state root
|
// import pre accounts & construct test genesis block & state root
|
||||||
db, _ := ethdb.NewMemDatabase()
|
db, _ := ethdb.NewMemDatabase()
|
||||||
if _, err := test.InsertPreState(db); err != nil {
|
gblock, err := t.genesis(config).Commit(db)
|
||||||
return fmt.Errorf("InsertPreState: %v", err)
|
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())
|
chain, err := core.NewBlockChain(db, config, ethash.NewShared(), new(event.TypeMux), vm.Config{})
|
||||||
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{})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer chain.Stop()
|
defer chain.Stop()
|
||||||
|
|
||||||
//vm.Debug = true
|
validBlocks, err := t.insertBlocks(chain)
|
||||||
validBlocks, err := test.TryBlocksInsert(chain)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
lastblockhash := common.HexToHash(test.lastblockhash)
|
|
||||||
cmlast := chain.LastBlockHash()
|
cmlast := chain.LastBlockHash()
|
||||||
if lastblockhash != cmlast {
|
if common.Hash(t.json.BestBlock) != cmlast {
|
||||||
return fmt.Errorf("lastblockhash validation mismatch: want: %x, have: %x", lastblockhash, cmlast)
|
return fmt.Errorf("last block hash validation mismatch: want: %x, have: %x", t.json.BestBlock, cmlast)
|
||||||
}
|
}
|
||||||
|
|
||||||
newDB, err := chain.State()
|
newDB, err := chain.State()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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 fmt.Errorf("post state validation failed: %v", err)
|
||||||
}
|
}
|
||||||
|
return t.validateImportedHeaders(chain, validBlocks)
|
||||||
return test.ValidateImportedHeaders(chain, validBlocks)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// InsertPreState populates the given database with the genesis
|
func (t *BlockTest) genesis(config *params.ChainConfig) *core.Genesis {
|
||||||
// accounts defined by the test.
|
return &core.Genesis{
|
||||||
func (t *BlockTest) InsertPreState(db ethdb.Database) (*state.StateDB, error) {
|
Config: config,
|
||||||
statedb, err := state.New(common.Hash{}, state.NewDatabase(db))
|
Nonce: t.json.Genesis.Nonce.Uint64(),
|
||||||
if err != nil {
|
Timestamp: t.json.Genesis.Timestamp.Uint64(),
|
||||||
return nil, err
|
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
|
/* 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
|
expected we are expected to ignore it and continue processing and then validate the
|
||||||
post state.
|
post state.
|
||||||
*/
|
*/
|
||||||
func (t *BlockTest) TryBlocksInsert(blockchain *core.BlockChain) ([]btBlock, error) {
|
func (t *BlockTest) insertBlocks(blockchain *core.BlockChain) ([]btBlock, error) {
|
||||||
validBlocks := make([]btBlock, 0)
|
validBlocks := make([]btBlock, 0)
|
||||||
// insert the test blocks, which will execute all transactions
|
// insert the test blocks, which will execute all transactions
|
||||||
for _, b := range t.Json.Blocks {
|
for _, b := range t.json.Blocks {
|
||||||
cb, err := mustConvertBlock(b)
|
cb, err := b.decode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if b.BlockHeader == nil {
|
if b.BlockHeader == nil {
|
||||||
continue // OK - block is supposed to be invalid, continue with next block
|
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 {
|
func validateHeader(h *btHeader, h2 *types.Header) error {
|
||||||
expectedBloom := mustConvertBytes(h.Bloom)
|
if h.Bloom != h2.Bloom {
|
||||||
if !bytes.Equal(expectedBloom, h2.Bloom.Bytes()) {
|
return fmt.Errorf("Bloom: want: %x have: %x", h.Bloom, h2.Bloom)
|
||||||
return fmt.Errorf("Bloom: want: %x have: %x", expectedBloom, h2.Bloom.Bytes())
|
|
||||||
}
|
}
|
||||||
|
if h.Coinbase != h2.Coinbase {
|
||||||
expectedCoinbase := mustConvertBytes(h.Coinbase)
|
return fmt.Errorf("Coinbase: want: %x have: %x", h.Coinbase, h2.Coinbase)
|
||||||
if !bytes.Equal(expectedCoinbase, h2.Coinbase.Bytes()) {
|
|
||||||
return fmt.Errorf("Coinbase: want: %x have: %x", expectedCoinbase, h2.Coinbase.Bytes())
|
|
||||||
}
|
}
|
||||||
|
if h.MixHash != h2.MixDigest {
|
||||||
expectedMixHashBytes := mustConvertBytes(h.MixHash)
|
return fmt.Errorf("MixHash: want: %x have: %x", h.MixHash, h2.MixDigest)
|
||||||
if !bytes.Equal(expectedMixHashBytes, h2.MixDigest.Bytes()) {
|
|
||||||
return fmt.Errorf("MixHash: want: %x have: %x", expectedMixHashBytes, h2.MixDigest.Bytes())
|
|
||||||
}
|
}
|
||||||
|
if h.Nonce != h2.Nonce {
|
||||||
expectedNonce := mustConvertBytes(h.Nonce)
|
return fmt.Errorf("Nonce: want: %x have: %x", h.Nonce, h2.Nonce)
|
||||||
if !bytes.Equal(expectedNonce, h2.Nonce[:]) {
|
|
||||||
return fmt.Errorf("Nonce: want: %x have: %x", expectedNonce, h2.Nonce)
|
|
||||||
}
|
}
|
||||||
|
if h.Number.Cmp(h2.Number) != 0 {
|
||||||
expectedNumber := mustConvertBigInt(h.Number, 16)
|
return fmt.Errorf("Number: want: %v have: %v", h.Number, h2.Number)
|
||||||
if expectedNumber.Cmp(h2.Number) != 0 {
|
|
||||||
return fmt.Errorf("Number: want: %v have: %v", expectedNumber, h2.Number)
|
|
||||||
}
|
}
|
||||||
|
if h.ParentHash != h2.ParentHash {
|
||||||
expectedParentHash := mustConvertBytes(h.ParentHash)
|
return fmt.Errorf("Parent hash: want: %x have: %x", h.ParentHash, h2.ParentHash)
|
||||||
if !bytes.Equal(expectedParentHash, h2.ParentHash.Bytes()) {
|
|
||||||
return fmt.Errorf("Parent hash: want: %x have: %x", expectedParentHash, h2.ParentHash.Bytes())
|
|
||||||
}
|
}
|
||||||
|
if h.ReceiptTrie != h2.ReceiptHash {
|
||||||
expectedReceiptHash := mustConvertBytes(h.ReceiptTrie)
|
return fmt.Errorf("Receipt hash: want: %x have: %x", h.ReceiptTrie, h2.ReceiptHash)
|
||||||
if !bytes.Equal(expectedReceiptHash, h2.ReceiptHash.Bytes()) {
|
|
||||||
return fmt.Errorf("Receipt hash: want: %x have: %x", expectedReceiptHash, h2.ReceiptHash.Bytes())
|
|
||||||
}
|
}
|
||||||
|
if h.TransactionsTrie != h2.TxHash {
|
||||||
expectedTxHash := mustConvertBytes(h.TransactionsTrie)
|
return fmt.Errorf("Tx hash: want: %x have: %x", h.TransactionsTrie, h2.TxHash)
|
||||||
if !bytes.Equal(expectedTxHash, h2.TxHash.Bytes()) {
|
|
||||||
return fmt.Errorf("Tx hash: want: %x have: %x", expectedTxHash, h2.TxHash.Bytes())
|
|
||||||
}
|
}
|
||||||
|
if h.StateRoot != h2.Root {
|
||||||
expectedStateHash := mustConvertBytes(h.StateRoot)
|
return fmt.Errorf("State hash: want: %x have: %x", h.StateRoot, h2.Root)
|
||||||
if !bytes.Equal(expectedStateHash, h2.Root.Bytes()) {
|
|
||||||
return fmt.Errorf("State hash: want: %x have: %x", expectedStateHash, h2.Root.Bytes())
|
|
||||||
}
|
}
|
||||||
|
if h.UncleHash != h2.UncleHash {
|
||||||
expectedUncleHash := mustConvertBytes(h.UncleHash)
|
return fmt.Errorf("Uncle hash: want: %x have: %x", h.UncleHash, h2.UncleHash)
|
||||||
if !bytes.Equal(expectedUncleHash, h2.UncleHash.Bytes()) {
|
|
||||||
return fmt.Errorf("Uncle hash: want: %x have: %x", expectedUncleHash, h2.UncleHash.Bytes())
|
|
||||||
}
|
}
|
||||||
|
if !bytes.Equal(h.ExtraData, h2.Extra) {
|
||||||
expectedExtraData := mustConvertBytes(h.ExtraData)
|
return fmt.Errorf("Extra data: want: %x have: %x", h.ExtraData, h2.Extra)
|
||||||
if !bytes.Equal(expectedExtraData, h2.Extra) {
|
|
||||||
return fmt.Errorf("Extra data: want: %x have: %x", expectedExtraData, h2.Extra)
|
|
||||||
}
|
}
|
||||||
|
if h.Difficulty.Cmp(h2.Difficulty) != 0 {
|
||||||
expectedDifficulty := mustConvertBigInt(h.Difficulty, 16)
|
return fmt.Errorf("Difficulty: want: %v have: %v", h.Difficulty, h2.Difficulty)
|
||||||
if expectedDifficulty.Cmp(h2.Difficulty) != 0 {
|
|
||||||
return fmt.Errorf("Difficulty: want: %v have: %v", expectedDifficulty, h2.Difficulty)
|
|
||||||
}
|
}
|
||||||
|
if h.GasLimit.Cmp(h2.GasLimit) != 0 {
|
||||||
expectedGasLimit := mustConvertBigInt(h.GasLimit, 16)
|
return fmt.Errorf("GasLimit: want: %v have: %v", h.GasLimit, h2.GasLimit)
|
||||||
if expectedGasLimit.Cmp(h2.GasLimit) != 0 {
|
|
||||||
return fmt.Errorf("GasLimit: want: %v have: %v", expectedGasLimit, h2.GasLimit)
|
|
||||||
}
|
}
|
||||||
expectedGasUsed := mustConvertBigInt(h.GasUsed, 16)
|
if h.GasUsed.Cmp(h2.GasUsed) != 0 {
|
||||||
if expectedGasUsed.Cmp(h2.GasUsed) != 0 {
|
return fmt.Errorf("GasUsed: want: %v have: %v", h.GasUsed, h2.GasUsed)
|
||||||
return fmt.Errorf("GasUsed: want: %v have: %v", expectedGasUsed, h2.GasUsed)
|
|
||||||
}
|
}
|
||||||
|
if h.Timestamp.Cmp(h2.Time) != 0 {
|
||||||
expectedTimestamp := mustConvertBigInt(h.Timestamp, 16)
|
return fmt.Errorf("Timestamp: want: %v have: %v", h.Timestamp, h2.Time)
|
||||||
if expectedTimestamp.Cmp(h2.Time) != 0 {
|
|
||||||
return fmt.Errorf("Timestamp: want: %v have: %v", expectedTimestamp, h2.Time)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
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
|
// validate post state accounts in test file against what we have in state db
|
||||||
for addrString, acct := range t.postAccounts {
|
for addr, acct := range t.json.Post {
|
||||||
// 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
|
|
||||||
}
|
|
||||||
|
|
||||||
// address is indirectly verified by the other fields, as it's the db key
|
// address is indirectly verified by the other fields, as it's the db key
|
||||||
code2 := statedb.GetCode(common.BytesToAddress(addr))
|
code2 := statedb.GetCode(addr)
|
||||||
balance2 := statedb.GetBalance(common.BytesToAddress(addr))
|
balance2 := statedb.GetBalance(addr)
|
||||||
nonce2 := statedb.GetNonce(common.BytesToAddress(addr))
|
nonce2 := statedb.GetNonce(addr)
|
||||||
if !bytes.Equal(code2, code) {
|
if !bytes.Equal(code2, acct.Code) {
|
||||||
return fmt.Errorf("account code mismatch for addr: %s want: %s have: %s", addrString, hex.EncodeToString(code), hex.EncodeToString(code2))
|
return fmt.Errorf("account code mismatch for addr: %s want: %v have: %s", addr, acct.Code, hex.EncodeToString(code2))
|
||||||
}
|
}
|
||||||
if balance2.Cmp(balance) != 0 {
|
if balance2.Cmp(acct.Balance) != 0 {
|
||||||
return fmt.Errorf("account balance mismatch for addr: %s, want: %d, have: %d", addrString, balance, balance2)
|
return fmt.Errorf("account balance mismatch for addr: %s, want: %d, have: %d", addr, acct.Balance, balance2)
|
||||||
}
|
}
|
||||||
if nonce2 != nonce {
|
if nonce2 != acct.Nonce {
|
||||||
return fmt.Errorf("account nonce mismatch for addr: %s want: %d have: %d", addrString, nonce, nonce2)
|
return fmt.Errorf("account nonce mismatch for addr: %s want: %d have: %d", addr, acct.Nonce, nonce2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
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)
|
// 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 {
|
for _, b := range validBlocks {
|
||||||
bmap[b.BlockHeader.Hash] = b
|
bmap[b.BlockHeader.Hash] = b
|
||||||
}
|
}
|
||||||
|
|
||||||
// iterate over blocks backwards from HEAD and validate imported
|
// iterate over blocks backwards from HEAD and validate imported
|
||||||
// headers vs test file. some tests have reorgs, and we import
|
// headers vs test file. some tests have reorgs, and we import
|
||||||
// block-by-block, so we can only validate imported headers after
|
// 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.
|
// 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) {
|
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[b.Hash()].BlockHeader, b.Header()); err != nil {
|
||||||
if err := validateHeader(bmap[bHash].BlockHeader, b.Header()); err != nil {
|
|
||||||
return fmt.Errorf("Imported block header validation failed: %v", err)
|
return fmt.Errorf("Imported block header validation failed: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func convertBlockTests(in map[string]*btJSON) (map[string]*BlockTest, error) {
|
func (bb *btBlock) decode() (*types.Block, error) {
|
||||||
out := make(map[string]*BlockTest)
|
data, err := hexutil.Decode(bb.Rlp)
|
||||||
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)
|
|
||||||
if err != nil {
|
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 nil, err
|
||||||
}
|
}
|
||||||
|
var b types.Block
|
||||||
return convertBlockTests(btjs)
|
err = rlp.DecodeBytes(data, &b)
|
||||||
}
|
return &b, err
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
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
|
package tests
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"path/filepath"
|
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestRLP(t *testing.T) {
|
func TestRLP(t *testing.T) {
|
||||||
err := RunRLPTest(filepath.Join(rlpTestDir, "rlptest.json"), nil)
|
t.Parallel()
|
||||||
if err != nil {
|
tm := new(testMatcher)
|
||||||
t.Fatal(err)
|
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)
|
||||||
|
}
|
||||||
func TestRLP_invalid(t *testing.T) {
|
})
|
||||||
err := RunRLPTest(filepath.Join(rlpTestDir, "invalidRLPTest.json"), nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,9 +21,7 @@ import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
"os"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
|
|
@ -44,33 +42,6 @@ type RLPTest struct {
|
||||||
Out string
|
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.
|
// Run executes the test.
|
||||||
func (t *RLPTest) Run() error {
|
func (t *RLPTest) Run() error {
|
||||||
outb, err := hex.DecodeString(t.Out)
|
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.
|
// This file is part of the go-ethereum library.
|
||||||
//
|
//
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
|
@ -18,180 +18,296 @@ package tests
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"math/big"
|
||||||
"strconv"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"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/common/math"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"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/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
func RunStateTestWithReader(chainConfig *params.ChainConfig, r io.Reader, skipTests []string) error {
|
// This table defines supported forks and their chain config.
|
||||||
tests := make(map[string]VmTest)
|
var stateTestForks = map[string]*params.ChainConfig{
|
||||||
if err := readJson(r, &tests); err != nil {
|
"Frontier": ¶ms.ChainConfig{
|
||||||
return err
|
ChainId: big.NewInt(1),
|
||||||
}
|
},
|
||||||
|
"Homestead": ¶ms.ChainConfig{
|
||||||
if err := runStateTests(chainConfig, tests, skipTests); err != nil {
|
HomesteadBlock: big.NewInt(0),
|
||||||
return err
|
ChainId: big.NewInt(1),
|
||||||
}
|
},
|
||||||
|
"EIP150": ¶ms.ChainConfig{
|
||||||
return nil
|
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 {
|
// StateTest checks transaction processing without block context.
|
||||||
tests := make(map[string]VmTest)
|
// See https://github.com/ethereum/EIPs/issues/176 for the test format specification.
|
||||||
if err := readJsonFile(p, &tests); err != nil {
|
type StateTest struct {
|
||||||
return err
|
json stJSON
|
||||||
}
|
|
||||||
|
|
||||||
if err := runStateTests(chainConfig, tests, skipTests); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchStateTest(chainConfig *params.ChainConfig, p string, conf bconf, b *testing.B) error {
|
// StateSubtest selects a specific configuration of a General State Test.
|
||||||
tests := make(map[string]VmTest)
|
type StateSubtest struct {
|
||||||
if err := readJsonFile(p, &tests); err != nil {
|
Fork string
|
||||||
return err
|
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 {
|
if !ok {
|
||||||
return fmt.Errorf("test not found: %s", conf.name)
|
return fmt.Errorf("no config for fork %q", subtest.Fork)
|
||||||
}
|
}
|
||||||
|
block, _ := t.genesis(config).ToBlock()
|
||||||
// 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()
|
|
||||||
db, _ := ethdb.NewMemDatabase()
|
db, _ := ethdb.NewMemDatabase()
|
||||||
statedb := makePreState(db, test.Pre)
|
statedb := makePreState(db, t.json.Pre)
|
||||||
b.StartTimer()
|
|
||||||
|
|
||||||
RunState(chainConfig, statedb, db, env, test.Exec)
|
post := t.json.Post[subtest.Fork][subtest.Index]
|
||||||
}
|
msg, err := t.json.Tx.toMessage(post)
|
||||||
|
|
||||||
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)
|
|
||||||
if err != nil {
|
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)
|
statedb.RevertToSnapshot(snapshot)
|
||||||
}
|
}
|
||||||
root, _ := statedb.CommitTo(db, chainConfig.IsEIP158(environment.Context.BlockNumber))
|
if post.Logs != nil {
|
||||||
return ret, statedb.Logs(), root, err
|
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 (
|
import (
|
||||||
"math/big"
|
"math/big"
|
||||||
"path/filepath"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestTransactions(t *testing.T) {
|
func TestTransaction(t *testing.T) {
|
||||||
config := ¶ms.ChainConfig{}
|
t.Parallel()
|
||||||
err := RunTransactionTests(config, filepath.Join(transactionTestDir, "ttTransactionTest.json"), TransSkipTests)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWrongRLPTransactions(t *testing.T) {
|
txt := new(testMatcher)
|
||||||
config := ¶ms.ChainConfig{}
|
txt.config(`^Homestead/`, params.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{
|
|
||||||
HomesteadBlock: big.NewInt(0),
|
HomesteadBlock: big.NewInt(0),
|
||||||
}
|
})
|
||||||
err := RunTransactionTests(config, filepath.Join(transactionTestDir, "Homestead", "ttTransactionTest.json"), TransSkipTests)
|
txt.config(`^EIP155/`, params.ChainConfig{
|
||||||
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),
|
|
||||||
HomesteadBlock: big.NewInt(0),
|
HomesteadBlock: big.NewInt(0),
|
||||||
|
EIP150Block: big.NewInt(0),
|
||||||
EIP155Block: big.NewInt(0),
|
EIP155Block: big.NewInt(0),
|
||||||
}
|
EIP158Block: 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{
|
|
||||||
ChainId: big.NewInt(1),
|
ChainId: big.NewInt(1),
|
||||||
HomesteadBlock: big.NewInt(0),
|
})
|
||||||
EIP155Block: big.NewInt(0),
|
txt.config(`^Metropolis/`, params.ChainConfig{
|
||||||
}
|
HomesteadBlock: big.NewInt(0),
|
||||||
err := RunTransactionTests(config, filepath.Join(transactionTestDir, "EIP155", "ttTransactionTestEip155VitaliksTests.json"), TransSkipTests)
|
EIP150Block: big.NewInt(0),
|
||||||
if err != nil {
|
EIP155Block: big.NewInt(0),
|
||||||
t.Fatal(err)
|
EIP158Block: big.NewInt(0),
|
||||||
}
|
MetropolisBlock: big.NewInt(0),
|
||||||
}
|
})
|
||||||
|
|
||||||
func TestTxEIP155VRule(t *testing.T) {
|
txt.walk(t, transactionTestDir, func(t *testing.T, name string, test *TransactionTest) {
|
||||||
config := ¶ms.ChainConfig{
|
cfg := txt.findConfig(name)
|
||||||
ChainId: big.NewInt(1),
|
if err := txt.checkFailure(t, name, test.Run(cfg)); err != nil {
|
||||||
HomesteadBlock: big.NewInt(0),
|
t.Error(err)
|
||||||
EIP155Block: big.NewInt(0),
|
}
|
||||||
}
|
})
|
||||||
err := RunTransactionTests(config, filepath.Join(transactionTestDir, "EIP155", "ttTransactionTestVRule.json"), TransSkipTests)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,208 +20,114 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"math/big"
|
||||||
"runtime"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"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/common/math"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Transaction Test JSON Format
|
// TransactionTest checks RLP decoding and sender derivation of transactions.
|
||||||
type TtTransaction struct {
|
|
||||||
Data string
|
|
||||||
GasLimit string
|
|
||||||
GasPrice string
|
|
||||||
Nonce string
|
|
||||||
R string
|
|
||||||
S string
|
|
||||||
To string
|
|
||||||
V string
|
|
||||||
Value string
|
|
||||||
}
|
|
||||||
|
|
||||||
type TransactionTest struct {
|
type TransactionTest struct {
|
||||||
Blocknumber string
|
json ttJSON
|
||||||
Rlp string
|
|
||||||
Sender string
|
|
||||||
Transaction TtTransaction
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func RunTransactionTestsWithReader(config *params.ChainConfig, r io.Reader, skipTests []string) error {
|
type ttJSON struct {
|
||||||
skipTest := make(map[string]bool, len(skipTests))
|
BlockNumber math.HexOrDecimal64 `json:"blockNumber"`
|
||||||
for _, name := range skipTests {
|
RLP hexutil.Bytes `json:"rlp"`
|
||||||
skipTest[name] = true
|
Sender hexutil.Bytes `json:"sender"`
|
||||||
}
|
Transaction *ttTransaction `json:"transaction"`
|
||||||
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func RunTransactionTests(config *params.ChainConfig, file string, skipTests []string) error {
|
//go:generate gencodec -type ttTransaction -field-override ttTransactionMarshaling -out gen_tttransaction.go
|
||||||
tests := make(map[string]TransactionTest)
|
|
||||||
if err := readJsonFile(file, &tests); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := runTransactionTests(config, tests, skipTests); err != nil {
|
type ttTransaction struct {
|
||||||
return err
|
Data []byte `gencodec:"required"`
|
||||||
}
|
GasLimit *big.Int `gencodec:"required"`
|
||||||
return nil
|
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 {
|
type ttTransactionMarshaling struct {
|
||||||
skipTest := make(map[string]bool, len(skipTests))
|
Data hexutil.Bytes
|
||||||
for _, name := range skipTests {
|
GasLimit *math.HexOrDecimal256
|
||||||
skipTest[name] = true
|
GasPrice *math.HexOrDecimal256
|
||||||
}
|
Nonce math.HexOrDecimal64
|
||||||
|
Value *math.HexOrDecimal256
|
||||||
for name, test := range tests {
|
R *math.HexOrDecimal256
|
||||||
// if the test should be skipped, return
|
S *math.HexOrDecimal256
|
||||||
if skipTest[name] {
|
V *math.HexOrDecimal256
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func runTransactionTest(config *params.ChainConfig, txTest TransactionTest) (err error) {
|
func (tt *TransactionTest) Run(config *params.ChainConfig) error {
|
||||||
tx := new(types.Transaction)
|
tx := new(types.Transaction)
|
||||||
err = rlp.DecodeBytes(mustConvertBytes(txTest.Rlp), tx)
|
if err := rlp.DecodeBytes(tt.json.RLP, tx); err != nil {
|
||||||
|
if tt.json.Transaction == nil {
|
||||||
if err != nil {
|
|
||||||
if txTest.Sender == "" {
|
|
||||||
// RLP decoding failed and this is expected (test OK)
|
|
||||||
return nil
|
return nil
|
||||||
} else {
|
} else {
|
||||||
// RLP decoding failed but is expected to succeed (test FAIL)
|
return fmt.Errorf("RLP decoding failed: %v", err)
|
||||||
return fmt.Errorf("RLP decoding failed when expected to succeed: %s", err)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Check sender derivation.
|
||||||
validationError := verifyTxFields(config, txTest, tx)
|
signer := types.MakeSigner(config, new(big.Int).SetUint64(uint64(tt.json.BlockNumber)))
|
||||||
if txTest.Sender == "" {
|
sender, err := types.Sender(signer, tx)
|
||||||
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)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if sender != common.BytesToAddress(tt.json.Sender) {
|
||||||
expectedSender := mustConvertAddress(txTest.Sender)
|
return fmt.Errorf("Sender mismatch: got %x, want %x", sender, tt.json.Sender)
|
||||||
if expectedSender != decodedSender {
|
|
||||||
return fmt.Errorf("Sender mismatch: %x %x", expectedSender, decodedSender)
|
|
||||||
}
|
}
|
||||||
|
// Check decoded fields.
|
||||||
expectedData := mustConvertBytes(txTest.Transaction.Data)
|
err = tt.json.Transaction.verify(signer, tx)
|
||||||
if !bytes.Equal(expectedData, decodedTx.Data()) {
|
if tt.json.Sender == nil && err == nil {
|
||||||
return fmt.Errorf("Tx input data mismatch: %#v %#v", expectedData, decodedTx.Data())
|
return errors.New("field validations succeeded but should fail")
|
||||||
}
|
}
|
||||||
|
if tt.json.Sender != nil && err != nil {
|
||||||
expectedGasLimit := mustConvertBigInt(txTest.Transaction.GasLimit, 16)
|
return fmt.Errorf("field validations failed after RLP decoding: %s", err)
|
||||||
if expectedGasLimit.Cmp(decodedTx.Gas()) != 0 {
|
}
|
||||||
return fmt.Errorf("GasLimit mismatch: %v %v", expectedGasLimit, decodedTx.Gas())
|
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
|
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
|
package tests
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
)
|
)
|
||||||
|
|
||||||
func BenchmarkVmAckermann32Tests(b *testing.B) {
|
func TestVM(t *testing.T) {
|
||||||
fn := filepath.Join(vmTestDir, "vmPerformanceTest.json")
|
t.Parallel()
|
||||||
if err := BenchVmTest(fn, bconf{"ackermann32", os.Getenv("JITFORCE") == "true", os.Getenv("JITVM") == "true"}, b); err != nil {
|
vmt := new(testMatcher)
|
||||||
b.Error(err)
|
vmt.fails("^vmSystemOperationsTest.json/createNameRegistrator$", "fails without parallel execution")
|
||||||
}
|
vmt.skipShortMode("^vmPerformanceTest.json")
|
||||||
}
|
vmt.skipShortMode("^vmInputLimits(Light)?.json")
|
||||||
|
|
||||||
func BenchmarkVmFibonacci16Tests(b *testing.B) {
|
vmt.walk(t, vmTestDir, func(t *testing.T, name string, test *VMTest) {
|
||||||
fn := filepath.Join(vmTestDir, "vmPerformanceTest.json")
|
withTrace(t, test.json.Exec.GasLimit, func(vmconfig vm.Config) error {
|
||||||
if err := BenchVmTest(fn, bconf{"fibonacci16", os.Getenv("JITFORCE") == "true", os.Getenv("JITVM") == "true"}, b); err != nil {
|
return vmt.checkFailure(t, name, test.Run(vmconfig))
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,218 +18,132 @@ package tests
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
"strconv"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"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/common/math"
|
||||||
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"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/core/vm"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
func RunVmTestWithReader(r io.Reader, skipTests []string) error {
|
// VMTest checks EVM execution without block or transaction context.
|
||||||
tests := make(map[string]VmTest)
|
// See https://github.com/ethereum/tests/wiki/VM-Tests for the test format specification.
|
||||||
err := readJson(r, &tests)
|
type VMTest struct {
|
||||||
if err != nil {
|
json vmJSON
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := runVmTests(tests, skipTests); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type bconf struct {
|
func (t *VMTest) UnmarshalJSON(data []byte) error {
|
||||||
name string
|
return json.Unmarshal(data, &t.json)
|
||||||
precomp bool
|
|
||||||
jit bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchVmTest(p string, conf bconf, b *testing.B) error {
|
type vmJSON struct {
|
||||||
tests := make(map[string]VmTest)
|
Env stEnv `json:"env"`
|
||||||
err := readJsonFile(p, &tests)
|
Exec vmExec `json:"exec"`
|
||||||
if err != nil {
|
Logs []stLog `json:"logs"`
|
||||||
return err
|
GasRemaining *math.HexOrDecimal64 `json:"gas"`
|
||||||
}
|
Out hexutil.Bytes `json:"out"`
|
||||||
|
Pre core.GenesisAlloc `json:"pre"`
|
||||||
test, ok := tests[conf.name]
|
Post core.GenesisAlloc `json:"post"`
|
||||||
if !ok {
|
PostStateRoot common.Hash `json:"postStateRoot"`
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func benchVmTest(test VmTest, env map[string]string, b *testing.B) {
|
//go:generate gencodec -type vmExec -field-override vmExecMarshaling -out gen_vmexec.go
|
||||||
b.StopTimer()
|
|
||||||
|
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()
|
db, _ := ethdb.NewMemDatabase()
|
||||||
statedb := makePreState(db, test.Pre)
|
statedb := makePreState(db, t.json.Pre)
|
||||||
b.StartTimer()
|
ret, gasRemaining, err := t.exec(statedb, vmconfig)
|
||||||
|
|
||||||
RunVm(statedb, env, test.Exec)
|
if t.json.GasRemaining == nil {
|
||||||
}
|
if err == nil {
|
||||||
|
return fmt.Errorf("gas unspecified (indicating an error), but VM returned no error")
|
||||||
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 gasRemaining > 0 {
|
||||||
if err := runVmTest(test); err != nil {
|
return fmt.Errorf("gas unspecified (indicating an error), but VM returned gas remaining > 0")
|
||||||
return fmt.Errorf("%s %s", name, err.Error())
|
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
log.Info(fmt.Sprint("VM test passed: ", name))
|
|
||||||
//fmt.Println(string(statedb.Dump()))
|
|
||||||
}
|
}
|
||||||
return nil
|
// 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)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
if gasRemaining != uint64(*t.json.GasRemaining) {
|
||||||
var (
|
return fmt.Errorf("remaining gas %v, want %v", gasRemaining, *t.json.GasRemaining)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
for addr, account := range t.json.Post {
|
||||||
// Check gas usage
|
for k, wantV := range account.Storage {
|
||||||
if len(test.Gas) == 0 && err == nil {
|
if haveV := statedb.GetState(addr, k); haveV != wantV {
|
||||||
return fmt.Errorf("gas unspecified, indicating an error. VM returned (incorrectly) successful")
|
return fmt.Errorf("wrong storage value at %x:\n got %x\n want %x", k, haveV, wantV)
|
||||||
} 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())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 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
|
func (t *VMTest) exec(statedb *state.StateDB, vmconfig vm.Config) ([]byte, uint64, error) {
|
||||||
if len(test.Logs) > 0 {
|
evm := t.newEVM(statedb, vmconfig)
|
||||||
lerr := checkLogs(test.Logs, logs)
|
e := t.json.Exec
|
||||||
if lerr != nil {
|
return evm.Call(vm.AccountRef(e.Caller), e.Address, e.Data, e.GasLimit, e.Value)
|
||||||
return lerr
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
|
transfer := func(db vm.StateDB, sender, recipient common.Address, amount *big.Int) {}
|
||||||
return nil
|
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) {
|
func vmTestBlockHash(n uint64) common.Hash {
|
||||||
chainConfig := ¶ms.ChainConfig{
|
return common.BytesToHash(crypto.Keccak256([]byte(big.NewInt(int64(n)).String())))
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue