helpers.go 5.47 KB
Newer Older
1 2 3 4 5 6 7 8 9
package tests

import (
	"io"

	"github.com/ethereum/go-ethereum/log"
	"github.com/stretchr/testify/require"

	"github.com/ethereum-optimism/optimism/cannon/mipsevm"
10
	"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
11
	"github.com/ethereum-optimism/optimism/cannon/mipsevm/multithreaded"
12
	mttestutil "github.com/ethereum-optimism/optimism/cannon/mipsevm/multithreaded/testutil"
13
	"github.com/ethereum-optimism/optimism/cannon/mipsevm/singlethreaded"
14
	sttestutil "github.com/ethereum-optimism/optimism/cannon/mipsevm/singlethreaded/testutil"
15 16 17
	"github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil"
)

18
type VMFactory func(po mipsevm.PreimageOracle, stdOut, stdErr io.Writer, log log.Logger, opts ...testutil.StateOption) mipsevm.FPVM
19

20
func singleThreadedVmFactory(po mipsevm.PreimageOracle, stdOut, stdErr io.Writer, log log.Logger, opts ...testutil.StateOption) mipsevm.FPVM {
21
	state := singlethreaded.CreateEmptyState()
22
	mutator := sttestutil.NewStateMutatorSingleThreaded(state)
23 24 25 26 27 28
	for _, opt := range opts {
		opt(mutator)
	}
	return singlethreaded.NewInstrumentedState(state, po, stdOut, stdErr, nil)
}

29
func multiThreadedVmFactory(po mipsevm.PreimageOracle, stdOut, stdErr io.Writer, log log.Logger, opts ...testutil.StateOption) mipsevm.FPVM {
30
	state := multithreaded.CreateEmptyState()
31
	mutator := mttestutil.NewStateMutatorMultiThreaded(state)
32 33 34
	for _, opt := range opts {
		opt(mutator)
	}
35
	return multithreaded.NewInstrumentedState(state, po, stdOut, stdErr, log, nil)
36 37 38 39 40
}

type ElfVMFactory func(t require.TestingT, elfFile string, po mipsevm.PreimageOracle, stdOut, stdErr io.Writer, log log.Logger) mipsevm.FPVM

func singleThreadElfVmFactory(t require.TestingT, elfFile string, po mipsevm.PreimageOracle, stdOut, stdErr io.Writer, log log.Logger) mipsevm.FPVM {
41 42 43 44
	state, meta := testutil.LoadELFProgram(t, elfFile, singlethreaded.CreateInitialState, true)
	fpvm := singlethreaded.NewInstrumentedState(state, po, stdOut, stdErr, meta)
	require.NoError(t, fpvm.InitDebug())
	return fpvm
45 46 47
}

func multiThreadElfVmFactory(t require.TestingT, elfFile string, po mipsevm.PreimageOracle, stdOut, stdErr io.Writer, log log.Logger) mipsevm.FPVM {
48
	state, meta := testutil.LoadELFProgram(t, elfFile, multithreaded.CreateInitialState, false)
49 50
	fpvm := multithreaded.NewInstrumentedState(state, po, stdOut, stdErr, log, meta)
	require.NoError(t, fpvm.InitDebug())
51
	return fpvm
52 53
}

54
type ProofGenerator func(t require.TestingT, state mipsevm.FPVMState, memoryProofAddresses ...arch.Word) []byte
55

56
func singleThreadedProofGenerator(t require.TestingT, state mipsevm.FPVMState, memoryProofAddresses ...arch.Word) []byte {
57 58 59 60 61 62 63 64 65 66 67 68 69
	var proofData []byte

	insnProof := state.GetMemory().MerkleProof(state.GetPC())
	proofData = append(proofData, insnProof[:]...)

	for _, addr := range memoryProofAddresses {
		memProof := state.GetMemory().MerkleProof(addr)
		proofData = append(proofData, memProof[:]...)
	}

	return proofData
}

70
func multiThreadedProofGenerator(t require.TestingT, state mipsevm.FPVMState, memoryProofAddresses ...arch.Word) []byte {
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
	mtState, ok := state.(*multithreaded.State)
	if !ok {
		require.Fail(t, "Failed to cast FPVMState to multithreaded State type")
	}

	proofData := mtState.EncodeThreadProof()
	insnProof := mtState.GetMemory().MerkleProof(mtState.GetPC())
	proofData = append(proofData, insnProof[:]...)

	for _, addr := range memoryProofAddresses {
		memProof := mtState.GetMemory().MerkleProof(addr)
		proofData = append(proofData, memProof[:]...)
	}

	return proofData
}

88
type VersionedVMTestCase struct {
89 90 91 92 93 94
	Name           string
	Contracts      *testutil.ContractMetadata
	StateHashFn    mipsevm.HashFn
	VMFactory      VMFactory
	ElfVMFactory   ElfVMFactory
	ProofGenerator ProofGenerator
95 96 97 98
}

func GetSingleThreadedTestCase(t require.TestingT) VersionedVMTestCase {
	return VersionedVMTestCase{
99 100 101 102 103
		Name:           "single-threaded",
		Contracts:      testutil.TestContractsSetup(t, testutil.MipsSingleThreaded),
		StateHashFn:    singlethreaded.GetStateHashFn(),
		VMFactory:      singleThreadedVmFactory,
		ElfVMFactory:   singleThreadElfVmFactory,
104
		ProofGenerator: singleThreadedProofGenerator,
105 106 107 108 109
	}
}

func GetMultiThreadedTestCase(t require.TestingT) VersionedVMTestCase {
	return VersionedVMTestCase{
110 111 112 113 114 115
		Name:           "multi-threaded",
		Contracts:      testutil.TestContractsSetup(t, testutil.MipsMultithreaded),
		StateHashFn:    multithreaded.GetStateHashFn(),
		VMFactory:      multiThreadedVmFactory,
		ElfVMFactory:   multiThreadElfVmFactory,
		ProofGenerator: multiThreadedProofGenerator,
116 117 118 119
	}
}

func GetMipsVersionTestCases(t require.TestingT) []VersionedVMTestCase {
120 121 122 123 124 125 126 127 128 129
	if arch.IsMips32 {
		return []VersionedVMTestCase{
			GetSingleThreadedTestCase(t),
			GetMultiThreadedTestCase(t),
		}
	} else {
		// 64-bit only supports MTCannon
		return []VersionedVMTestCase{
			GetMultiThreadedTestCase(t),
		}
130 131
	}
}
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148

type threadProofTestcase struct {
	Name  string
	Proof []byte
}

func GenerateEmptyThreadProofVariations(t require.TestingT) []threadProofTestcase {
	defaultThreadProof := multiThreadedProofGenerator(t, multithreaded.CreateEmptyState())
	zeroBytesThreadProof := make([]byte, multithreaded.THREAD_WITNESS_SIZE)
	copy(zeroBytesThreadProof[multithreaded.SERIALIZED_THREAD_SIZE:], defaultThreadProof[multithreaded.SERIALIZED_THREAD_SIZE:])
	nilBytesThreadProof := defaultThreadProof[multithreaded.SERIALIZED_THREAD_SIZE:]
	return []threadProofTestcase{
		{Name: "default thread proof", Proof: defaultThreadProof},
		{Name: "zeroed thread bytes proof", Proof: zeroBytesThreadProof},
		{Name: "nil thread bytes proof", Proof: nilBytesThreadProof},
	}
}