5157d4540a
This adds the ability to run --state.fork=Merged, and have post-merge rules apply. When doing so, it also requires the input env to contain currentRandom, and enforces the currentDifficulty to be omitted or zero.
494 lines
12 KiB
Go
494 lines
12 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/docker/docker/pkg/reexec"
|
|
"github.com/ethereum/go-ethereum/cmd/evm/internal/t8ntool"
|
|
"github.com/ethereum/go-ethereum/internal/cmdtest"
|
|
)
|
|
|
|
func TestMain(m *testing.M) {
|
|
// Run the app if we've been exec'd as "ethkey-test" in runEthkey.
|
|
reexec.Register("evm-test", func() {
|
|
if err := app.Run(os.Args); err != nil {
|
|
fmt.Fprintln(os.Stderr, err)
|
|
os.Exit(1)
|
|
}
|
|
os.Exit(0)
|
|
})
|
|
// check if we have been reexec'd
|
|
if reexec.Init() {
|
|
return
|
|
}
|
|
os.Exit(m.Run())
|
|
}
|
|
|
|
type testT8n struct {
|
|
*cmdtest.TestCmd
|
|
}
|
|
|
|
type t8nInput struct {
|
|
inAlloc string
|
|
inTxs string
|
|
inEnv string
|
|
stFork string
|
|
stReward string
|
|
}
|
|
|
|
func (args *t8nInput) get(base string) []string {
|
|
var out []string
|
|
if opt := args.inAlloc; opt != "" {
|
|
out = append(out, "--input.alloc")
|
|
out = append(out, fmt.Sprintf("%v/%v", base, opt))
|
|
}
|
|
if opt := args.inTxs; opt != "" {
|
|
out = append(out, "--input.txs")
|
|
out = append(out, fmt.Sprintf("%v/%v", base, opt))
|
|
}
|
|
if opt := args.inEnv; opt != "" {
|
|
out = append(out, "--input.env")
|
|
out = append(out, fmt.Sprintf("%v/%v", base, opt))
|
|
}
|
|
if opt := args.stFork; opt != "" {
|
|
out = append(out, "--state.fork", opt)
|
|
}
|
|
if opt := args.stReward; opt != "" {
|
|
out = append(out, "--state.reward", opt)
|
|
}
|
|
return out
|
|
}
|
|
|
|
type t8nOutput struct {
|
|
alloc bool
|
|
result bool
|
|
body bool
|
|
}
|
|
|
|
func (args *t8nOutput) get() (out []string) {
|
|
if args.body {
|
|
out = append(out, "--output.body", "stdout")
|
|
} else {
|
|
out = append(out, "--output.body", "") // empty means ignore
|
|
}
|
|
if args.result {
|
|
out = append(out, "--output.result", "stdout")
|
|
} else {
|
|
out = append(out, "--output.result", "")
|
|
}
|
|
if args.alloc {
|
|
out = append(out, "--output.alloc", "stdout")
|
|
} else {
|
|
out = append(out, "--output.alloc", "")
|
|
}
|
|
return out
|
|
}
|
|
|
|
func TestT8n(t *testing.T) {
|
|
tt := new(testT8n)
|
|
tt.TestCmd = cmdtest.NewTestCmd(t, tt)
|
|
for i, tc := range []struct {
|
|
base string
|
|
input t8nInput
|
|
output t8nOutput
|
|
expExitCode int
|
|
expOut string
|
|
}{
|
|
{ // Test exit (3) on bad config
|
|
base: "./testdata/1",
|
|
input: t8nInput{
|
|
"alloc.json", "txs.json", "env.json", "Frontier+1346", "",
|
|
},
|
|
output: t8nOutput{alloc: true, result: true},
|
|
expExitCode: 3,
|
|
},
|
|
{
|
|
base: "./testdata/1",
|
|
input: t8nInput{
|
|
"alloc.json", "txs.json", "env.json", "Byzantium", "",
|
|
},
|
|
output: t8nOutput{alloc: true, result: true},
|
|
expOut: "exp.json",
|
|
},
|
|
{ // blockhash test
|
|
base: "./testdata/3",
|
|
input: t8nInput{
|
|
"alloc.json", "txs.json", "env.json", "Berlin", "",
|
|
},
|
|
output: t8nOutput{alloc: true, result: true},
|
|
expOut: "exp.json",
|
|
},
|
|
{ // missing blockhash test
|
|
base: "./testdata/4",
|
|
input: t8nInput{
|
|
"alloc.json", "txs.json", "env.json", "Berlin", "",
|
|
},
|
|
output: t8nOutput{alloc: true, result: true},
|
|
expExitCode: 4,
|
|
},
|
|
{ // Uncle test
|
|
base: "./testdata/5",
|
|
input: t8nInput{
|
|
"alloc.json", "txs.json", "env.json", "Byzantium", "0x80",
|
|
},
|
|
output: t8nOutput{alloc: true, result: true},
|
|
expOut: "exp.json",
|
|
},
|
|
{ // Sign json transactions
|
|
base: "./testdata/13",
|
|
input: t8nInput{
|
|
"alloc.json", "txs.json", "env.json", "London", "",
|
|
},
|
|
output: t8nOutput{body: true},
|
|
expOut: "exp.json",
|
|
},
|
|
{ // Already signed transactions
|
|
base: "./testdata/13",
|
|
input: t8nInput{
|
|
"alloc.json", "signed_txs.rlp", "env.json", "London", "",
|
|
},
|
|
output: t8nOutput{result: true},
|
|
expOut: "exp2.json",
|
|
},
|
|
{ // Difficulty calculation - no uncles
|
|
base: "./testdata/14",
|
|
input: t8nInput{
|
|
"alloc.json", "txs.json", "env.json", "London", "",
|
|
},
|
|
output: t8nOutput{result: true},
|
|
expOut: "exp.json",
|
|
},
|
|
{ // Difficulty calculation - with uncles
|
|
base: "./testdata/14",
|
|
input: t8nInput{
|
|
"alloc.json", "txs.json", "env.uncles.json", "London", "",
|
|
},
|
|
output: t8nOutput{result: true},
|
|
expOut: "exp2.json",
|
|
},
|
|
{ // Difficulty calculation - with ommers + Berlin
|
|
base: "./testdata/14",
|
|
input: t8nInput{
|
|
"alloc.json", "txs.json", "env.uncles.json", "Berlin", "",
|
|
},
|
|
output: t8nOutput{result: true},
|
|
expOut: "exp_berlin.json",
|
|
},
|
|
{ // Difficulty calculation on arrow glacier
|
|
base: "./testdata/19",
|
|
input: t8nInput{
|
|
"alloc.json", "txs.json", "env.json", "London", "",
|
|
},
|
|
output: t8nOutput{result: true},
|
|
expOut: "exp_london.json",
|
|
},
|
|
{ // Difficulty calculation on arrow glacier
|
|
base: "./testdata/19",
|
|
input: t8nInput{
|
|
"alloc.json", "txs.json", "env.json", "ArrowGlacier", "",
|
|
},
|
|
output: t8nOutput{result: true},
|
|
expOut: "exp_arrowglacier.json",
|
|
},
|
|
{ // Sign unprotected (pre-EIP155) transaction
|
|
base: "./testdata/23",
|
|
input: t8nInput{
|
|
"alloc.json", "txs.json", "env.json", "Berlin", "",
|
|
},
|
|
output: t8nOutput{result: true},
|
|
expOut: "exp.json",
|
|
},
|
|
{ // Test post-merge transition
|
|
base: "./testdata/24",
|
|
input: t8nInput{
|
|
"alloc.json", "txs.json", "env.json", "Merged", "",
|
|
},
|
|
output: t8nOutput{alloc: true, result: true},
|
|
expOut: "exp.json",
|
|
},
|
|
{ // Test post-merge transition where input is missing random
|
|
base: "./testdata/24",
|
|
input: t8nInput{
|
|
"alloc.json", "txs.json", "env-missingrandom.json", "Merged", "",
|
|
},
|
|
output: t8nOutput{alloc: false, result: false},
|
|
expExitCode: 3,
|
|
},
|
|
} {
|
|
|
|
args := []string{"t8n"}
|
|
args = append(args, tc.output.get()...)
|
|
args = append(args, tc.input.get(tc.base)...)
|
|
var qArgs []string // quoted args for debugging purposes
|
|
for _, arg := range args {
|
|
if len(arg) == 0 {
|
|
qArgs = append(qArgs, `""`)
|
|
} else {
|
|
qArgs = append(qArgs, arg)
|
|
}
|
|
}
|
|
tt.Logf("args: %v\n", strings.Join(qArgs, " "))
|
|
tt.Run("evm-test", args...)
|
|
// Compare the expected output, if provided
|
|
if tc.expOut != "" {
|
|
want, err := os.ReadFile(fmt.Sprintf("%v/%v", tc.base, tc.expOut))
|
|
if err != nil {
|
|
t.Fatalf("test %d: could not read expected output: %v", i, err)
|
|
}
|
|
have := tt.Output()
|
|
ok, err := cmpJson(have, want)
|
|
switch {
|
|
case err != nil:
|
|
t.Fatalf("test %d, json parsing failed: %v", i, err)
|
|
case !ok:
|
|
t.Fatalf("test %d: output wrong, have \n%v\nwant\n%v\n", i, string(have), string(want))
|
|
}
|
|
}
|
|
tt.WaitExit()
|
|
if have, want := tt.ExitStatus(), tc.expExitCode; have != want {
|
|
t.Fatalf("test %d: wrong exit code, have %d, want %d", i, have, want)
|
|
}
|
|
}
|
|
}
|
|
|
|
type t9nInput struct {
|
|
inTxs string
|
|
stFork string
|
|
}
|
|
|
|
func (args *t9nInput) get(base string) []string {
|
|
var out []string
|
|
if opt := args.inTxs; opt != "" {
|
|
out = append(out, "--input.txs")
|
|
out = append(out, fmt.Sprintf("%v/%v", base, opt))
|
|
}
|
|
if opt := args.stFork; opt != "" {
|
|
out = append(out, "--state.fork", opt)
|
|
}
|
|
return out
|
|
}
|
|
|
|
func TestT9n(t *testing.T) {
|
|
tt := new(testT8n)
|
|
tt.TestCmd = cmdtest.NewTestCmd(t, tt)
|
|
for i, tc := range []struct {
|
|
base string
|
|
input t9nInput
|
|
expExitCode int
|
|
expOut string
|
|
}{
|
|
{ // London txs on homestead
|
|
base: "./testdata/15",
|
|
input: t9nInput{
|
|
inTxs: "signed_txs.rlp",
|
|
stFork: "Homestead",
|
|
},
|
|
expOut: "exp.json",
|
|
},
|
|
{ // London txs on London
|
|
base: "./testdata/15",
|
|
input: t9nInput{
|
|
inTxs: "signed_txs.rlp",
|
|
stFork: "London",
|
|
},
|
|
expOut: "exp2.json",
|
|
},
|
|
{ // An RLP list (a blockheader really)
|
|
base: "./testdata/15",
|
|
input: t9nInput{
|
|
inTxs: "blockheader.rlp",
|
|
stFork: "London",
|
|
},
|
|
expOut: "exp3.json",
|
|
},
|
|
{ // Transactions with too low gas
|
|
base: "./testdata/16",
|
|
input: t9nInput{
|
|
inTxs: "signed_txs.rlp",
|
|
stFork: "London",
|
|
},
|
|
expOut: "exp.json",
|
|
},
|
|
{ // Transactions with value exceeding 256 bits
|
|
base: "./testdata/17",
|
|
input: t9nInput{
|
|
inTxs: "signed_txs.rlp",
|
|
stFork: "London",
|
|
},
|
|
expOut: "exp.json",
|
|
},
|
|
{ // Invalid RLP
|
|
base: "./testdata/18",
|
|
input: t9nInput{
|
|
inTxs: "invalid.rlp",
|
|
stFork: "London",
|
|
},
|
|
expExitCode: t8ntool.ErrorIO,
|
|
},
|
|
} {
|
|
|
|
args := []string{"t9n"}
|
|
args = append(args, tc.input.get(tc.base)...)
|
|
|
|
tt.Run("evm-test", args...)
|
|
tt.Logf("args:\n go run . %v\n", strings.Join(args, " "))
|
|
// Compare the expected output, if provided
|
|
if tc.expOut != "" {
|
|
want, err := os.ReadFile(fmt.Sprintf("%v/%v", tc.base, tc.expOut))
|
|
if err != nil {
|
|
t.Fatalf("test %d: could not read expected output: %v", i, err)
|
|
}
|
|
have := tt.Output()
|
|
ok, err := cmpJson(have, want)
|
|
switch {
|
|
case err != nil:
|
|
t.Logf(string(have))
|
|
t.Fatalf("test %d, json parsing failed: %v", i, err)
|
|
case !ok:
|
|
t.Fatalf("test %d: output wrong, have \n%v\nwant\n%v\n", i, string(have), string(want))
|
|
}
|
|
}
|
|
tt.WaitExit()
|
|
if have, want := tt.ExitStatus(), tc.expExitCode; have != want {
|
|
t.Fatalf("test %d: wrong exit code, have %d, want %d", i, have, want)
|
|
}
|
|
}
|
|
}
|
|
|
|
type b11rInput struct {
|
|
inEnv string
|
|
inOmmersRlp string
|
|
inTxsRlp string
|
|
inClique string
|
|
ethash bool
|
|
ethashMode string
|
|
ethashDir string
|
|
}
|
|
|
|
func (args *b11rInput) get(base string) []string {
|
|
var out []string
|
|
if opt := args.inEnv; opt != "" {
|
|
out = append(out, "--input.header")
|
|
out = append(out, fmt.Sprintf("%v/%v", base, opt))
|
|
}
|
|
if opt := args.inOmmersRlp; opt != "" {
|
|
out = append(out, "--input.ommers")
|
|
out = append(out, fmt.Sprintf("%v/%v", base, opt))
|
|
}
|
|
if opt := args.inTxsRlp; opt != "" {
|
|
out = append(out, "--input.txs")
|
|
out = append(out, fmt.Sprintf("%v/%v", base, opt))
|
|
}
|
|
if opt := args.inClique; opt != "" {
|
|
out = append(out, "--seal.clique")
|
|
out = append(out, fmt.Sprintf("%v/%v", base, opt))
|
|
}
|
|
if args.ethash {
|
|
out = append(out, "--seal.ethash")
|
|
}
|
|
if opt := args.ethashMode; opt != "" {
|
|
out = append(out, "--seal.ethash.mode")
|
|
out = append(out, fmt.Sprintf("%v/%v", base, opt))
|
|
}
|
|
if opt := args.ethashDir; opt != "" {
|
|
out = append(out, "--seal.ethash.dir")
|
|
out = append(out, fmt.Sprintf("%v/%v", base, opt))
|
|
}
|
|
out = append(out, "--output.block")
|
|
out = append(out, "stdout")
|
|
return out
|
|
}
|
|
|
|
func TestB11r(t *testing.T) {
|
|
tt := new(testT8n)
|
|
tt.TestCmd = cmdtest.NewTestCmd(t, tt)
|
|
for i, tc := range []struct {
|
|
base string
|
|
input b11rInput
|
|
expExitCode int
|
|
expOut string
|
|
}{
|
|
{ // unsealed block
|
|
base: "./testdata/20",
|
|
input: b11rInput{
|
|
inEnv: "header.json",
|
|
inOmmersRlp: "ommers.json",
|
|
inTxsRlp: "txs.rlp",
|
|
},
|
|
expOut: "exp.json",
|
|
},
|
|
{ // ethash test seal
|
|
base: "./testdata/21",
|
|
input: b11rInput{
|
|
inEnv: "header.json",
|
|
inOmmersRlp: "ommers.json",
|
|
inTxsRlp: "txs.rlp",
|
|
},
|
|
expOut: "exp.json",
|
|
},
|
|
{ // clique test seal
|
|
base: "./testdata/21",
|
|
input: b11rInput{
|
|
inEnv: "header.json",
|
|
inOmmersRlp: "ommers.json",
|
|
inTxsRlp: "txs.rlp",
|
|
inClique: "clique.json",
|
|
},
|
|
expOut: "exp-clique.json",
|
|
},
|
|
{ // block with ommers
|
|
base: "./testdata/22",
|
|
input: b11rInput{
|
|
inEnv: "header.json",
|
|
inOmmersRlp: "ommers.json",
|
|
inTxsRlp: "txs.rlp",
|
|
},
|
|
expOut: "exp.json",
|
|
},
|
|
} {
|
|
|
|
args := []string{"b11r"}
|
|
args = append(args, tc.input.get(tc.base)...)
|
|
|
|
tt.Run("evm-test", args...)
|
|
tt.Logf("args:\n go run . %v\n", strings.Join(args, " "))
|
|
// Compare the expected output, if provided
|
|
if tc.expOut != "" {
|
|
want, err := os.ReadFile(fmt.Sprintf("%v/%v", tc.base, tc.expOut))
|
|
if err != nil {
|
|
t.Fatalf("test %d: could not read expected output: %v", i, err)
|
|
}
|
|
have := tt.Output()
|
|
ok, err := cmpJson(have, want)
|
|
switch {
|
|
case err != nil:
|
|
t.Logf(string(have))
|
|
t.Fatalf("test %d, json parsing failed: %v", i, err)
|
|
case !ok:
|
|
t.Fatalf("test %d: output wrong, have \n%v\nwant\n%v\n", i, string(have), string(want))
|
|
}
|
|
}
|
|
tt.WaitExit()
|
|
if have, want := tt.ExitStatus(), tc.expExitCode; have != want {
|
|
t.Fatalf("test %d: wrong exit code, have %d, want %d", i, have, want)
|
|
}
|
|
}
|
|
}
|
|
|
|
// cmpJson compares the JSON in two byte slices.
|
|
func cmpJson(a, b []byte) (bool, error) {
|
|
var j, j2 interface{}
|
|
if err := json.Unmarshal(a, &j); err != nil {
|
|
return false, err
|
|
}
|
|
if err := json.Unmarshal(b, &j2); err != nil {
|
|
return false, err
|
|
}
|
|
return reflect.DeepEqual(j2, j), nil
|
|
}
|