diff --git a/core/vm/evm_test.go b/core/vm/evm_test.go new file mode 100644 index 000000000000..54e4c112a51f --- /dev/null +++ b/core/vm/evm_test.go @@ -0,0 +1,132 @@ +// Copyright 2017 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 . + +package vm + +import ( + "math/big" + "testing" + + "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/rawdb" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/params" +) + +var contractCheckStakingTests = []struct { + codeSize uint + staked int64 + failure error +}{ + {params.MaxCodeSizeSoft, 0, nil}, // no need to stake + {params.MaxCodeSizeSoft + 1, 0, ErrCodeInsufficientStake}, //reading code size > threshold, need to stake + {params.MaxCodeSizeSoft + 1, int64(params.CodeStakingPerChunk - 1), ErrCodeInsufficientStake}, // not enough staking + {params.MaxCodeSizeSoft + 1, int64(params.CodeStakingPerChunk), nil}, // barely enough staking + {params.MaxCodeSizeSoft * 2, int64(params.CodeStakingPerChunk), nil}, + {params.MaxCodeSizeSoft*2 + 1, int64(params.CodeStakingPerChunk*2 - 1), ErrCodeInsufficientStake}, + {params.MaxCodeSizeSoft*2 + 1, int64(params.CodeStakingPerChunk * 2), nil}, +} + +func TestContractCheckStakingW3IP002(t *testing.T) { + caddr := common.BytesToAddress([]byte("contract")) + calls := []string{"call", "callCode", "delegateCall"} + for _, callMethod := range calls { + for i, tt := range contractCheckStakingTests { + statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) + statedb.CreateAccount(caddr) + statedb.SetCode(caddr, codegenWithSize(tt.codeSize)) + + vmctx := BlockContext{ + BlockNumber: big.NewInt(0), + CanTransfer: func(_ StateDB, _ common.Address, toAmount *big.Int) bool { + return big.NewInt(tt.staked).Cmp(toAmount) >= 0 + }, + Transfer: func(StateDB, common.Address, common.Address, *big.Int) {}, + } + vmenv := NewEVM(vmctx, TxContext{}, statedb, params.AllEthashProtocolChanges, Config{}) + + caller := AccountRef(caddr) + var err error + if callMethod == "call" { + _, _, err = vmenv.Call(AccountRef(common.Address{}), caddr, nil, math.MaxUint64, new(big.Int)) + } else if callMethod == "callCode" { + _, _, err = vmenv.CallCode(caller, caddr, nil, math.MaxUint64, new(big.Int)) + } else if callMethod == "delegateCall" { + _, _, err = vmenv.DelegateCall(NewContract(caller, caller, big.NewInt(0), 0), caddr, nil, math.MaxUint64) + } else { + panic("invalid call method") + } + + if err != tt.failure { + t.Errorf("test %d: failure mismatch: have %v, want %v", i, err, tt.failure) + } + } + } +} + +var createTests = []struct { + pushByte byte + codeSizeHex string + staked int64 + usedGas uint64 + failure error +}{ + {byte(PUSH1), "0xff", 0, 51030, nil}, // no need to stake + {byte(PUSH2), "0x6000", 0, 4918662, nil}, // no need to stake + {byte(PUSH2), "0x6001", 0, math.MaxUint64, ErrCodeInsufficientStake}, // code size > soft limit, have to stake + {byte(PUSH2), "0x6001", int64(params.CodeStakingPerChunk), 4918668, nil}, // staked + {byte(PUSH2), "0xc000", int64(params.CodeStakingPerChunk), 4924422, nil}, // size = soft limit * 2, creation gas capped + {byte(PUSH2), "0xc001", int64(params.CodeStakingPerChunk), math.MaxUint64, ErrCodeInsufficientStake}, + {byte(PUSH2), "0xc001", int64(params.CodeStakingPerChunk*2 - 1), math.MaxUint64, ErrCodeInsufficientStake}, + {byte(PUSH2), "0xc001", int64(params.CodeStakingPerChunk * 2), 4924431, nil}, +} + +func TestCreateW3IP002(t *testing.T) { + addr := common.BytesToAddress([]byte("caller")) + for i, tt := range createTests { + statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) + + // PUSHx , PUSH1 00, RETURN + // to manipulate how much data should be stored as code + code := []byte{tt.pushByte} + code = append(code, hexutil.MustDecode(tt.codeSizeHex)...) + code = append(code, hexutil.MustDecode("0x6000f3")...) // PUSH1 00, RETURN + + vmctx := BlockContext{ + BlockNumber: big.NewInt(0), + CanTransfer: func(_ StateDB, _ common.Address, toAmount *big.Int) bool { + return big.NewInt(tt.staked).Cmp(toAmount) >= 0 + }, + Transfer: func(StateDB, common.Address, common.Address, *big.Int) {}, + } + vmenv := NewEVM(vmctx, TxContext{}, statedb, params.AllEthashProtocolChanges, Config{}) + + _, _, leftOverGas, err := vmenv.Create( + AccountRef(addr), + code, + math.MaxUint64, + big.NewInt(0), + ) + if err != tt.failure { + t.Errorf("test %d: failure mismatch: have %v, want %v", i, err, tt.failure) + } + if used := math.MaxUint64 - leftOverGas; used != tt.usedGas { + t.Errorf("test %d: gas used mismatch: have %v, want %v", i, used, tt.usedGas) + } + } +}