32150f8aa9
This PR adds support for ArrowGlacier, as defined by https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/arrow-glacier.md https://eips.ethereum.org/EIPS/eip-4345 > Starting with FORK_BLOCK_NUMBER the client will calculate the difficulty based on a fake block number suggesting to the client that the difficulty bomb is adjusting 10,700,000 blocks later than the actual block number. This also adds support for evm t8n to return the calculated difficulty, so it can be used to construct test.
350 lines
8.3 KiB
Go
350 lines
8.3 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,
|
|
},
|
|
{ // Ommer 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 uncles + 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",
|
|
},
|
|
} {
|
|
|
|
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)
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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
|
|
}
|