Skip to content

Commit

Permalink
Adds vm tests (vechain#660)
Browse files Browse the repository at this point in the history
* Adds contract tests

* Adds evm tests

* Add test files for memory, noop, opcodes and stack

* Add gas tests

* Add memory table tests

* Add gas_table and interpreter tests

* Renames packages and handles return value

* Removes unused function

* Fixes linter problems

* Implement Tony's suggestions

* simplify noop tracer

---------

Co-authored-by: Makis Christou <[email protected]>
Co-authored-by: tony <[email protected]>
  • Loading branch information
3 people authored Feb 29, 2024
1 parent d1dcdea commit 45d9e89
Show file tree
Hide file tree
Showing 10 changed files with 1,175 additions and 2 deletions.
127 changes: 127 additions & 0 deletions vm/contracts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import (
"time"

"github.com/ethereum/go-ethereum/common"
"github.com/holiman/uint256"
"github.com/stretchr/testify/assert"
)

// precompiledTest defines the input/output pairs for precompiled contract tests.
Expand Down Expand Up @@ -297,3 +299,128 @@ func loadJson(name string) ([]precompiledTest, error) {
err = json.Unmarshal(data, &testcases)
return testcases, err
}

func TestAsDelegate(t *testing.T) {
// Mock addresses
parentCallerAddress := common.HexToAddress("0x01")
objectAddress := common.HexToAddress("0x03")

// Create a parent contract to act as the caller
parentContract := NewContract(AccountRef(parentCallerAddress), AccountRef(parentCallerAddress), big.NewInt(2000), 5000)

// Create a child contract, which will be turned into a delegate
childContract := NewContract(parentContract, AccountRef(objectAddress), big.NewInt(2000), 5000)

// Call AsDelegate on the child contract
delegatedContract := childContract.AsDelegate()

// Perform your test assertions
assert.True(t, delegatedContract.DelegateCall, "Contract should be in delegate call mode")
assert.Equal(t, parentContract.CallerAddress, delegatedContract.CallerAddress, "Caller address should match parent contract caller address")
assert.Equal(t, parentContract.value, delegatedContract.value, "Value should match parent contract value")
}

func TestValidJumpdest(t *testing.T) {
// Example bytecode: PUSH1 0x02 JUMPDEST STOP
code := []byte{0x60, 0x02, 0x5b, 0x00}

contract := &Contract{
Code: code,
}

// Test a valid jump destination (position of JUMPDEST opcode)
validDest := uint256.NewInt(2)
assert.True(t, contract.validJumpdest(validDest), "Expected valid jump destination")

// Test an invalid jump destination (within PUSH1 data)
invalidDest := uint256.NewInt(1)
assert.False(t, contract.validJumpdest(invalidDest), "Expected invalid jump destination due to being within PUSH data")

// Test an invalid jump destination (non-existent opcode)
nonExistentDest := uint256.NewInt(100)
assert.False(t, contract.validJumpdest(nonExistentDest), "Expected invalid jump destination due to non-existent opcode")

// Test a non-JUMPDEST opcode (STOP opcode)
nonJumpdestOpcode := uint256.NewInt(3)
assert.False(t, contract.validJumpdest(nonJumpdestOpcode), "Expected invalid jump destination due to non-JUMPDEST opcode")

// Test edge cases
// Destination right at the start of the code
startOfCode := uint256.NewInt(0)
assert.False(t, contract.validJumpdest(startOfCode), "Expected invalid jump destination at the start of the code")

// Destination right at the end of the code
endOfCode := uint256.NewInt(uint64(len(code) - 1))
assert.False(t, contract.validJumpdest(endOfCode), "Expected invalid jump destination at the end of the code")
}

func TestIsCode(t *testing.T) {
// Example bytecode: PUSH1 0x02 JUMPDEST STOP
code := []byte{0x60, 0x02, 0x5b, 0x00}

contract := &Contract{
Code: code,
}

// Test when analysis is not set
assert.False(t, contract.isCode(1), "Position 1 should not be valid code")
assert.True(t, contract.isCode(2), "Position 2 should be valid code")

// Test that analysis is now set after calling isCode
assert.NotNil(t, contract.analysis, "Analysis should be set after calling isCode")
}

func setupContract() *Contract {
return &Contract{
CallerAddress: common.HexToAddress("0x01"),
value: big.NewInt(1000),
Code: []byte{0x60, 0x02, 0x5b, 0x00}, // Example bytecode
CodeHash: common.HexToHash("somehash"),
CodeAddr: new(common.Address),
}
}

func TestGetOp(t *testing.T) {
contract := setupContract()
assert.Equal(t, OpCode(0x60), contract.GetOp(0), "Expected OpCode at position 0 to match")
assert.Equal(t, OpCode(0x5b), contract.GetOp(2), "Expected OpCode at position 2 to match")
}

func TestGetByte(t *testing.T) {
contract := setupContract()
assert.Equal(t, byte(0x60), contract.GetByte(0), "Expected byte at position 0 to match")
assert.Equal(t, byte(0x00), contract.GetByte(3), "Expected byte at position 3 to match")
assert.Equal(t, byte(0x00), contract.GetByte(10), "Expected byte at out of bounds position to be 0")
}

func TestCaller(t *testing.T) {
contract := setupContract()
assert.Equal(t, common.HexToAddress("0x01"), contract.Caller(), "Expected caller address to match")
}

func TestValue(t *testing.T) {
contract := setupContract()
assert.Equal(t, big.NewInt(1000), contract.Value(), "Expected value to match")
}

func TestSetCode(t *testing.T) {
contract := setupContract()
newCode := []byte{0x01, 0x02}
newHash := common.HexToHash("newhash")
contract.SetCode(newHash, newCode)

assert.Equal(t, newCode, contract.Code, "Expected code to be updated")
assert.Equal(t, newHash, contract.CodeHash, "Expected code hash to be updated")
}

func TestSetCallCode(t *testing.T) {
contract := setupContract()
newCode := []byte{0x03, 0x04}
newHash := common.HexToHash("newerhash")
newAddr := common.HexToAddress("0x02")
contract.SetCallCode(&newAddr, newHash, newCode)

assert.Equal(t, newCode, contract.Code, "Expected code to be updated")
assert.Equal(t, newHash, contract.CodeHash, "Expected codehash to be updated")
assert.Equal(t, &newAddr, contract.CodeAddr, "Expected code address to be updated")
}
159 changes: 159 additions & 0 deletions vm/evm_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
package vm

import (
"math/big"
"testing"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/params"
"github.com/holiman/uint256"
"github.com/stretchr/testify/assert"
)

var _ Logger = (*noopTracer)(nil)

type noopTracer struct{}

func (t *noopTracer) CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
}
func (t *noopTracer) CaptureEnd(output []byte, gasUsed uint64, err error) {
}
func (t *noopTracer) CaptureState(pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, rData []byte, depth int, err error) {
}
func (t *noopTracer) CaptureFault(pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) {
}
func (t *noopTracer) CaptureEnter(typ OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
}
func (t *noopTracer) CaptureExit(output []byte, gasUsed uint64, err error) {
}
func (*noopTracer) CaptureClauseStart(gasLimit uint64) {}
func (*noopTracer) CaptureClauseEnd(restGas uint64) {}

func setupEvmTestContract(codeAddr *common.Address) (*EVM, *Contract) {
statedb := NoopStateDB{}

evmConfig := Config{
Tracer: &noopTracer{},
}

evm := NewEVM(
Context{
BlockNumber: big.NewInt(1),
GasPrice: big.NewInt(1),
CanTransfer: NoopCanTransfer,
Transfer: NoopTransfer,
NewContractAddress: newContractAddress,
},
statedb,
&ChainConfig{ChainConfig: *params.TestChainConfig}, evmConfig)

contract := &Contract{
CallerAddress: common.HexToAddress("0x01"),
Code: []byte{0x60, 0x02, 0x5b, 0x00},
CodeHash: common.HexToHash("somehash"),
CodeAddr: codeAddr,
Gas: 500000,
DelegateCall: true,
}

contractCode := []byte{0x60, 0x00}
contract.SetCode(common.BytesToHash(contractCode), contractCode)

return evm, contract
}

func TestCall(t *testing.T) {
codeAddr := common.BytesToAddress([]byte{1})
evm, _ := setupEvmTestContract(&codeAddr)

caller := AccountRef(common.HexToAddress("0x01"))
contractAddr := common.HexToAddress("0x1")
input := []byte{}

ret, leftOverGas, err := evm.Call(caller, contractAddr, input, 1000000, big.NewInt(100000))

assert.Nil(t, err)
assert.Nil(t, ret)
assert.NotNil(t, leftOverGas)
}

func TestCallCode(t *testing.T) {
codeAddr := common.BytesToAddress([]byte{1})
evm, _ := setupEvmTestContract(&codeAddr)

caller := AccountRef(common.HexToAddress("0x01"))
contractAddr := common.HexToAddress("0x1")
input := []byte{}

ret, leftOverGas, err := evm.CallCode(caller, contractAddr, input, 1000000, big.NewInt(100000))

assert.Nil(t, err)
assert.Nil(t, ret)
assert.NotNil(t, leftOverGas)
}

func TestDelegateCall(t *testing.T) {
codeAddr := common.BytesToAddress([]byte{1})
evm, _ := setupEvmTestContract(&codeAddr)

parentCallerAddress := common.HexToAddress("0x01")
objectAddress := common.HexToAddress("0x03")
input := []byte{}

parentContract := NewContract(AccountRef(parentCallerAddress), AccountRef(parentCallerAddress), big.NewInt(2000), 5000)
childContract := NewContract(parentContract, AccountRef(objectAddress), big.NewInt(2000), 5000)

ret, leftOverGas, err := evm.DelegateCall(childContract, parentContract.CallerAddress, input, 1000000)

assert.Nil(t, err)
assert.Nil(t, ret)
assert.NotNil(t, leftOverGas)
}

func TestStaticCall(t *testing.T) {
codeAddr := common.BytesToAddress([]byte{1})
evm, _ := setupEvmTestContract(&codeAddr)

parentCallerAddress := common.HexToAddress("0x01")
objectAddress := common.HexToAddress("0x03")
input := []byte{}

parentContract := NewContract(AccountRef(parentCallerAddress), AccountRef(parentCallerAddress), big.NewInt(2000), 5000)
childContract := NewContract(parentContract, AccountRef(objectAddress), big.NewInt(2000), 5000)

ret, leftOverGas, err := evm.StaticCall(childContract, parentContract.CallerAddress, input, 1000000)

assert.Nil(t, err)
assert.Nil(t, ret)
assert.NotNil(t, leftOverGas)
}

func TestCreate(t *testing.T) {
codeAddr := common.BytesToAddress([]byte{1})
evm, _ := setupEvmTestContract(&codeAddr)

parentCallerAddress := common.HexToAddress("0x01234567A")
input := []byte{}

ret, addr, leftOverGas, err := evm.Create(AccountRef(parentCallerAddress), input, 1000000, big.NewInt(2000))

assert.Nil(t, err)
assert.NotNil(t, addr)
assert.Nil(t, ret)
assert.NotNil(t, leftOverGas)
}

func TestCreate2(t *testing.T) {
codeAddr := common.BytesToAddress([]byte{1})
evm, _ := setupEvmTestContract(&codeAddr)

parentCallerAddress := common.HexToAddress("0x01234567A")
input := []byte{}

ret, addr, leftOverGas, err := evm.Create2(AccountRef(parentCallerAddress), input, 10000, big.NewInt(2000), uint256.NewInt(10000))

assert.Nil(t, err)
assert.NotNil(t, addr)
assert.Nil(t, ret)
assert.NotNil(t, leftOverGas)
}
Loading

0 comments on commit 45d9e89

Please sign in to comment.