Skip to content
This repository has been archived by the owner on Aug 13, 2019. It is now read-only.

EIP 145 - Shift opcodes #43

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions cmd/evm/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,10 @@ func (ruleSet) IsAtlantis(*big.Int) bool {
return true
}

func (ruleSet) IsAgharta(*big.Int) bool {
return true
}

func (ruleSet) GasTable(*big.Int) *vm.GasTable {
return &vm.GasTable{
ExtcodeSize: big.NewInt(700),
Expand Down
9 changes: 9 additions & 0 deletions core/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,15 @@ func (c *ChainConfig) IsAtlantis(num *big.Int) bool {
return num.Cmp(fork.Block) >= 0
}

// IsAgharta returns true if num is greater than Agharta config block
func (c *ChainConfig) IsAgharta(num *big.Int) bool {
fork := c.ForkByName("Agharta")
if fork.Block == nil || num == nil {
return false
}
return num.Cmp(fork.Block) >= 0
}

// ForkByName looks up a Fork by its name, assumed to be unique
func (c *ChainConfig) ForkByName(name string) *Fork {
for i := range c.Forks {
Expand Down
4 changes: 2 additions & 2 deletions core/execution.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ var (
callCreateDepthMax = 1024 // limit call/create stack
errCallCreateDepth = fmt.Errorf("Max call depth exceeded (%d)", callCreateDepthMax)

maxCodeSize = 24576
maxCodeSize = 24576
errMaxCodeSizeExceeded = fmt.Errorf("Max Code Size exceeded (%d)", maxCodeSize)
)

Expand Down Expand Up @@ -152,7 +152,7 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A
// When an error was returned by the EVM or when setting the creation code
// above we revert to the snapshot and consume any gas remaining. Additionally
// when we're in homestead this also counts for code storage gas errors.
if createAccount && maxCodeSizeExceeded ||(err != nil && (env.RuleSet().IsHomestead(env.BlockNumber()) || err != vm.CodeStoreOutOfGasError)) {
if createAccount && maxCodeSizeExceeded || (err != nil && (env.RuleSet().IsHomestead(env.BlockNumber()) || err != vm.CodeStoreOutOfGasError)) {
env.RevertToSnapshot(snapshotPreTransfer)
if err != vm.ErrRevert {
contract.UseGas(contract.Gas)
Expand Down
1 change: 1 addition & 0 deletions core/vm/environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
type RuleSet interface {
IsHomestead(*big.Int) bool
IsAtlantis(*big.Int) bool
IsAgharta(*big.Int) bool
// GasTable returns the gas prices for this phase, which is based on
// block number passed in.
GasTable(*big.Int) *GasTable
Expand Down
3 changes: 3 additions & 0 deletions core/vm/gas.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,9 @@ var _baseCheck = map[OpCode]req{
XOR: {2, GasFastestStep, 1},
NOT: {1, GasFastestStep, 1},
BYTE: {2, GasFastestStep, 1},
SHL: {2, GasFastestStep, 1},
SHR: {2, GasFastestStep, 1},
SAR: {2, GasFastestStep, 1},
CALLDATALOAD: {1, GasFastestStep, 1},
CALLDATACOPY: {3, GasFastestStep, 1},
MLOAD: {1, GasFastestStep, 1},
Expand Down
44 changes: 44 additions & 0 deletions core/vm/instructions.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (

"github.com/eth-classic/go-ethereum/common"
"github.com/eth-classic/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/common/math"
Copy link
Contributor

@steviezhang steviezhang Jun 5, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove autoimport

)

var callStipend = big.NewInt(2300) // Free gas given at beginning of call.
Expand Down Expand Up @@ -244,6 +245,49 @@ func opMulmod(instr instruction, pc *uint64, env Environment, contract *Contract
}
}

func opSHL(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Build is failing because the function signature doesn't match the updated. Add the ([]byte, error) return to the signature and return those values respectively (nil returns for all changed functions including the opSha3 that was changed)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is instr instruction added as an argument?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's what it used to be, this is the old function signature

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's also preventing build since he's passing these in as instrFn in the jump table, so either we remove that field or we add an additional field to instrFn and all the other opcodes

shift, value := math.U256(stack.pop()), math.U256(stack.pop())

if shift.Cmp(big.NewInt(256)) >= 0 {
value.SetUint64(0)
} else {
n := uint(shift.Uint64())
math.U256(value.Lsh(value, n))
}
stack.push(value)
}

func opSHR(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
shift, value := math.U256(stack.pop()), math.U256(stack.pop())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove references to math package


if shift.Cmp(big.NewInt(256)) >= 0 {
value.SetUint64(0)
} else {
n := uint(shift.Uint64())
math.U256(value.Rsh(value, n))
}
stack.push(value)
}

func opSAR(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
shift, value := math.U256(stack.pop()), math.S256(stack.pop())

if shift.Cmp(big.NewInt(256)) >= 0 {
if value.Sign() >= 0 {
value.SetUint64(0)
} else {
value.SetInt64(-1)
}

stack.push(math.U256(value))
} else {
n := uint(shift.Uint64())
// value
value.Rsh(value, n)
stack.push(math.U256(value))
}
}

func opSha3(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
offset, size := stack.pop(), stack.pop()
hash := crypto.Keccak256(memory.Get(offset.Int64(), size.Int64()))
Expand Down
182 changes: 182 additions & 0 deletions core/vm/instructions_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
// 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 <http://www.gnu.org/licenses/>.

package vm

import (
"math/big"
"testing"

"github.com/eth-classic/go-ethereum/common"
)

type TwoOperandTestcase struct {
X string
Y string
Expected string
}

type twoOperandParams struct {
x string
y string
}

var commonParams []*twoOperandParams
var twoOpMethods map[string]instrFn

func init() {

// Params is a list of common edgecases that should be used for some common tests
params := []string{
"0000000000000000000000000000000000000000000000000000000000000000", // 0
"0000000000000000000000000000000000000000000000000000000000000001", // +1
"0000000000000000000000000000000000000000000000000000000000000005", // +5
"7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe", // + max -1
"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", // + max
"8000000000000000000000000000000000000000000000000000000000000000", // - max
"8000000000000000000000000000000000000000000000000000000000000001", // - max+1
"fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb", // - 5
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", // - 1
}
// Params are combined so each param is used on each 'side'
commonParams = make([]*twoOperandParams, len(params)*len(params))
for i, x := range params {
for j, y := range params {
commonParams[i*len(params)+j] = &twoOperandParams{x, y}
}
}
twoOpMethods = map[string]instrFn{
"add": opAdd,
"sub": opSub,
"mul": opMul,
"div": opDiv,
"sdiv": opSdiv,
"mod": opMod,
"smod": opSmod,
"exp": opExp,
"signext": opSignExtend,
"lt": opLt,
"gt": opGt,
"slt": opSlt,
"sgt": opSgt,
"eq": opEq,
"and": opAnd,
"or": opOr,
"xor": opXor,
"byte": opByte,
"shl": opSHL,
"shr": opSHR,
"sar": opSAR,
}
}

func testTwoOperandOp(t *testing.T, tests []TwoOperandTestcase, opFn instrFn, name string) {

var (
stack = newstack()
pc = uint64(0)
)

for i, test := range tests {
x := new(big.Int).SetBytes(common.Hex2Bytes(test.X))
y := new(big.Int).SetBytes(common.Hex2Bytes(test.Y))
expected := new(big.Int).SetBytes(common.Hex2Bytes(test.Expected))
stack.push(x)
stack.push(y)
opFn(instruction{}, &pc, nil, nil, nil, stack)
actual := stack.pop()

if actual.Cmp(expected) != 0 {
t.Errorf("Testcase %v %d, %v(%x, %x): expected %x, got %x", name, i, name, x, y, expected, actual)
} else {
t.Log("Worked")
}

}
}

func TestByteOp(t *testing.T) {
tests := []TwoOperandTestcase{
{"ABCDEF0908070605040302010000000000000000000000000000000000000000", "00", "AB"},
{"ABCDEF0908070605040302010000000000000000000000000000000000000000", "01", "CD"},
{"00CDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff", "00", "00"},
{"00CDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff", "01", "CD"},
{"0000000000000000000000000000000000000000000000000000000000102030", "1F", "30"},
{"0000000000000000000000000000000000000000000000000000000000102030", "1E", "20"},
{"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "20", "00"},
{"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "FFFFFFFFFFFFFFFF", "00"},
}
testTwoOperandOp(t, tests, opByte, "byte")
}

func TestSHL(t *testing.T) {
// Testcases from https://github.com/ethereum/EIPs/blob/master/EIPS/eip-145.md#shl-shift-left
tests := []TwoOperandTestcase{
{"0000000000000000000000000000000000000000000000000000000000000001", "01", "0000000000000000000000000000000000000000000000000000000000000002"},
{"0000000000000000000000000000000000000000000000000000000000000001", "ff", "8000000000000000000000000000000000000000000000000000000000000000"},
{"0000000000000000000000000000000000000000000000000000000000000001", "0100", "0000000000000000000000000000000000000000000000000000000000000000"},
{"0000000000000000000000000000000000000000000000000000000000000001", "0101", "0000000000000000000000000000000000000000000000000000000000000000"},
{"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "00", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"},
{"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "01", "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe"},
{"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "ff", "8000000000000000000000000000000000000000000000000000000000000000"},
{"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0100", "0000000000000000000000000000000000000000000000000000000000000000"},
{"0000000000000000000000000000000000000000000000000000000000000000", "01", "0000000000000000000000000000000000000000000000000000000000000000"},
{"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "01", "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe"},
}
testTwoOperandOp(t, tests, opSHL, "shl")
}

func TestSHR(t *testing.T) {
// Testcases from https://github.com/ethereum/EIPs/blob/master/EIPS/eip-145.md#shr-logical-shift-right
tests := []TwoOperandTestcase{
{"0000000000000000000000000000000000000000000000000000000000000001", "00", "0000000000000000000000000000000000000000000000000000000000000001"},
{"0000000000000000000000000000000000000000000000000000000000000001", "01", "0000000000000000000000000000000000000000000000000000000000000000"},
{"8000000000000000000000000000000000000000000000000000000000000000", "01", "4000000000000000000000000000000000000000000000000000000000000000"},
{"8000000000000000000000000000000000000000000000000000000000000000", "ff", "0000000000000000000000000000000000000000000000000000000000000001"},
{"8000000000000000000000000000000000000000000000000000000000000000", "0100", "0000000000000000000000000000000000000000000000000000000000000000"},
{"8000000000000000000000000000000000000000000000000000000000000000", "0101", "0000000000000000000000000000000000000000000000000000000000000000"},
{"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "00", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"},
{"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "01", "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"},
{"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "ff", "0000000000000000000000000000000000000000000000000000000000000001"},
{"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0100", "0000000000000000000000000000000000000000000000000000000000000000"},
{"0000000000000000000000000000000000000000000000000000000000000000", "01", "0000000000000000000000000000000000000000000000000000000000000000"},
}
testTwoOperandOp(t, tests, opSHR, "shr")
}

func TestSAR(t *testing.T) {
// Testcases from https://github.com/ethereum/EIPs/blob/master/EIPS/eip-145.md#sar-arithmetic-shift-right
tests := []TwoOperandTestcase{
{"0000000000000000000000000000000000000000000000000000000000000001", "00", "0000000000000000000000000000000000000000000000000000000000000001"},
{"0000000000000000000000000000000000000000000000000000000000000001", "01", "0000000000000000000000000000000000000000000000000000000000000000"},
{"8000000000000000000000000000000000000000000000000000000000000000", "01", "c000000000000000000000000000000000000000000000000000000000000000"},
{"8000000000000000000000000000000000000000000000000000000000000000", "ff", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"},
{"8000000000000000000000000000000000000000000000000000000000000000", "0100", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"},
{"8000000000000000000000000000000000000000000000000000000000000000", "0101", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"},
{"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "00", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"},
{"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "01", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"},
{"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "ff", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"},
{"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0100", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"},
{"0000000000000000000000000000000000000000000000000000000000000000", "01", "0000000000000000000000000000000000000000000000000000000000000000"},
{"4000000000000000000000000000000000000000000000000000000000000000", "fe", "0000000000000000000000000000000000000000000000000000000000000001"},
{"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "f8", "000000000000000000000000000000000000000000000000000000000000007f"},
{"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "fe", "0000000000000000000000000000000000000000000000000000000000000001"},
{"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "ff", "0000000000000000000000000000000000000000000000000000000000000000"},
{"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0100", "0000000000000000000000000000000000000000000000000000000000000000"},
}

testTwoOperandOp(t, tests, opSAR, "sar")
}
Copy link
Contributor

@austinabell austinabell May 31, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did it not work out cleanly to add in the tests from the testdata directory (from ETH as well) to include with these? These tests included should be sufficient, just curious

17 changes: 17 additions & 0 deletions core/vm/jump_table.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,23 @@ func newJumpTable(ruleset RuleSet, blockNumber *big.Int) vmJumpTable {
}
}

if ruleset.IsAgharta(blockNumber) {
jumpTable[SHL] = jumpPtr{
fn: opSHL,
valid: true,
}

jumpTable[SHR] = jumpPtr{
fn: opSHR,
valid: true,
}

jumpTable[SAR] = jumpPtr{
fn: opSAR,
valid: true,
}
}

return jumpTable
}

Expand Down
7 changes: 5 additions & 2 deletions core/vm/jump_table_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,15 @@ import (
type ruleSet struct {
hs *big.Int
at *big.Int
ag *big.Int
}

func (r ruleSet) IsHomestead(n *big.Int) bool { return n.Cmp(r.hs) >= 0 }

func (r ruleSet) IsAtlantis(n *big.Int) bool { return n.Cmp(r.at) >= 0 }

func (r ruleSet) IsAgharta(n *big.Int) bool { return n.Cmp(r.ag) >= 0 }

func (r ruleSet) GasTable(*big.Int) *GasTable {
return &GasTable{
ExtcodeSize: big.NewInt(20),
Expand All @@ -43,13 +46,13 @@ func (r ruleSet) GasTable(*big.Int) *GasTable {
}

func TestInit(t *testing.T) {
jumpTable := newJumpTable(ruleSet{big.NewInt(1), big.NewInt(1)}, big.NewInt(0))
jumpTable := newJumpTable(ruleSet{big.NewInt(1), big.NewInt(1), big.NewInt(1)}, big.NewInt(0))
if jumpTable[DELEGATECALL].valid {
t.Error("Expected DELEGATECALL not to be present")
}

for _, n := range []int64{1, 2, 100} {
jumpTable := newJumpTable(ruleSet{big.NewInt(1), big.NewInt(1)}, big.NewInt(n))
jumpTable := newJumpTable(ruleSet{big.NewInt(1), big.NewInt(1), big.NewInt(1)}, big.NewInt(n))
if !jumpTable[DELEGATECALL].valid {
t.Error("Expected DELEGATECALL to be present for block", n)
}
Expand Down
9 changes: 9 additions & 0 deletions core/vm/opcodes.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ const (
XOR
NOT
BYTE
SHL
SHR
SAR

SHA3 = 0x20
)
Expand Down Expand Up @@ -232,6 +235,9 @@ var opCodeToString = map[OpCode]string{
OR: "OR",
XOR: "XOR",
BYTE: "BYTE",
SHL: "SHL",
SHR: "SHR",
SAR: "SAR",
ADDMOD: "ADDMOD",
MULMOD: "MULMOD",

Expand Down Expand Up @@ -395,6 +401,9 @@ var stringToOp = map[string]OpCode{
"OR": OR,
"XOR": XOR,
"BYTE": BYTE,
"SHL": SHL,
"SHR": SHR,
"SAR": SAR,
"ADDMOD": ADDMOD,
"MULMOD": MULMOD,
"SHA3": SHA3,
Expand Down
1 change: 1 addition & 0 deletions core/vm/runtime/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ type ruleSet struct{}

func (ruleSet) IsHomestead(*big.Int) bool { return true }
func (ruleSet) IsAtlantis(*big.Int) bool { return true }
func (ruleSet) IsAgharta(*big.Int) bool { return true }
func (ruleSet) GasTable(*big.Int) *vm.GasTable {
return &vm.GasTable{
ExtcodeSize: big.NewInt(700),
Expand Down
Loading