core/vm/program: review concerns from @lightclient

pull/30725/head
Martin Holst Swende 10 hours ago
parent a103de4eff
commit 3a3e7fd7aa
No known key found for this signature in database
GPG Key ID: 683B438C05A5DDF0
  1. 147
      core/vm/program/program.go
  2. 25
      core/vm/program/program_test.go
  3. 27
      core/vm/runtime/runtime_test.go

@ -39,6 +39,7 @@ type Program struct {
code []byte code []byte
} }
// New creates a new Program
func New() *Program { func New() *Program {
return &Program{ return &Program{
code: make([]byte, 0), code: make([]byte, 0),
@ -54,21 +55,17 @@ func (p *Program) add(op byte) *Program {
// pushBig creates a PUSHX instruction and pushes the given val. // pushBig creates a PUSHX instruction and pushes the given val.
// - If the val is nil, it pushes zero // - If the val is nil, it pushes zero
// - If the val is bigger than 32 bytes, it panics // - If the val is bigger than 32 bytes, it panics
func (p *Program) pushBig(val *big.Int) { func (p *Program) doPush(val *uint256.Int) {
if val == nil { if val == nil {
val = big.NewInt(0) val = new(uint256.Int)
} }
valBytes := val.Bytes() valBytes := val.Bytes()
if len(valBytes) == 0 { if len(valBytes) == 0 {
valBytes = append(valBytes, 0) valBytes = append(valBytes, 0)
} }
bLen := len(valBytes) bLen := len(valBytes)
if bLen > 32 {
panic(fmt.Sprintf("Push value too large, %d bytes", bLen))
}
p.add(byte(vm.PUSH1) - 1 + byte(bLen)) p.add(byte(vm.PUSH1) - 1 + byte(bLen))
p.Append(valBytes) p.Append(valBytes)
} }
// Append appends the given data to the code. // Append appends the given data to the code.
@ -77,67 +74,77 @@ func (p *Program) Append(data []byte) *Program {
return p return p
} }
// Op appends the given opcode // Bytes returns the Program bytecode. OBS: This is not a copy.
func (p *Program) Op(op vm.OpCode) *Program { func (p *Program) Bytes() []byte {
return p.add(byte(op)) return p.code
}
// SetBytes sets the Program bytecode. The combination of Bytes and SetBytes means
// that external callers can implement missing functionality:
//
// ...
// prog.Push(1)
// code := prog.Bytes()
// manipulate(code)
// prog.SetBytes(code)
func (p *Program) SetBytes(code []byte) {
p.code = code
}
// Hex returns the Program bytecode as a hex string.
func (p *Program) Hex() string {
return fmt.Sprintf("%02x", p.Bytes())
} }
// Ops appends the given opcodes // Op appends the given opcode(s).
func (p *Program) Ops(ops ...vm.OpCode) *Program { func (p *Program) Op(ops ...vm.OpCode) *Program {
for _, op := range ops { for _, op := range ops {
p.add(byte(op)) p.add(byte(op))
} }
return p return p
} }
// Push creates a PUSHX instruction with the data provided // Push creates a PUSHX instruction with the data provided. If zero is being pushed,
// PUSH0 will be avoided in favour of [PUSH1 0], to ensure backwards compatibility.
func (p *Program) Push(val any) *Program { func (p *Program) Push(val any) *Program {
switch v := val.(type) { switch v := val.(type) {
case int: case int:
p.pushBig(new(big.Int).SetUint64(uint64(v))) p.doPush(new(uint256.Int).SetUint64(uint64(v)))
case uint64: case uint64:
p.pushBig(new(big.Int).SetUint64(v)) p.doPush(new(uint256.Int).SetUint64(v))
case uint32: case uint32:
p.pushBig(new(big.Int).SetUint64(uint64(v))) p.doPush(new(uint256.Int).SetUint64(uint64(v)))
case uint16:
p.doPush(new(uint256.Int).SetUint64(uint64(v)))
case *big.Int: case *big.Int:
p.pushBig(v) p.doPush(uint256.MustFromBig(v))
case *uint256.Int: case *uint256.Int:
p.pushBig(v.ToBig()) p.doPush(v)
case uint256.Int: case uint256.Int:
p.pushBig(v.ToBig()) p.doPush(&v)
case []byte: case []byte:
p.pushBig(new(big.Int).SetBytes(v)) p.doPush(new(uint256.Int).SetBytes(v))
case byte: case byte:
p.pushBig(new(big.Int).SetUint64(uint64(v))) p.doPush(new(uint256.Int).SetUint64(uint64(v)))
case interface{ Bytes() []byte }: case interface{ Bytes() []byte }:
// Here, we jump through some hovm in order to avoid depending on // Here, we jump through some hoops in order to avoid depending on
// go-ethereum types.Address and common.Hash, and instead use the // go-ethereum types.Address and common.Hash, and instead use the
// interface. This works on both values and pointers! // interface. This works on both values and pointers!
p.pushBig(new(big.Int).SetBytes(v.Bytes())) p.doPush(new(uint256.Int).SetBytes(v.Bytes()))
case nil: case nil:
p.pushBig(nil) p.doPush(nil)
default: default:
panic(fmt.Sprintf("unsupported type %v", v)) panic(fmt.Sprintf("unsupported type %T", v))
} }
return p return p
} }
// Push0 implements PUSH0 (0x5f) // Push0 implements PUSH0 (0x5f).
func (p *Program) Push0() *Program { func (p *Program) Push0() *Program {
return p.Op(vm.PUSH0) return p.Op(vm.PUSH0)
} }
// Bytes returns the Program bytecode // ExtcodeCopy performs an extcodecopy invocation.
func (p *Program) Bytes() []byte {
return p.code
}
// Hex returns the Program bytecode as a hex string
func (p *Program) Hex() string {
return fmt.Sprintf("%02x", p.Bytes())
}
// ExtcodeCopy performsa an extcodecopy invocation
func (p *Program) ExtcodeCopy(address, memOffset, codeOffset, length any) *Program { func (p *Program) ExtcodeCopy(address, memOffset, codeOffset, length any) *Program {
p.Push(length) p.Push(length)
p.Push(codeOffset) p.Push(codeOffset)
@ -148,9 +155,9 @@ func (p *Program) ExtcodeCopy(address, memOffset, codeOffset, length any) *Progr
// Call is a convenience function to make a call. If 'gas' is nil, the opcode GAS will // Call is a convenience function to make a call. If 'gas' is nil, the opcode GAS will
// be used to provide all gas. // be used to provide all gas.
func (p *Program) Call(gas *big.Int, address, value, inOffset, inSize, outOffset, outSize any) *Program { func (p *Program) Call(gas *uint256.Int, address, value, inOffset, inSize, outOffset, outSize any) *Program {
if outOffset == outSize && inSize == outSize && inOffset == outSize && value == outSize { if outOffset == outSize && inSize == outSize && inOffset == outSize && value == outSize {
p.Push(outSize).Ops(vm.DUP1, vm.DUP1, vm.DUP1, vm.DUP1) p.Push(outSize).Op(vm.DUP1, vm.DUP1, vm.DUP1, vm.DUP1)
} else { } else {
p.Push(outSize).Push(outOffset).Push(inSize).Push(inOffset).Push(value) p.Push(outSize).Push(outOffset).Push(inSize).Push(inOffset).Push(value)
} }
@ -158,16 +165,16 @@ func (p *Program) Call(gas *big.Int, address, value, inOffset, inSize, outOffset
if gas == nil { if gas == nil {
p.Op(vm.GAS) p.Op(vm.GAS)
} else { } else {
p.pushBig(gas) p.doPush(gas)
} }
return p.Op(vm.CALL) return p.Op(vm.CALL)
} }
// DelegateCall is a convenience function to make a delegatecall. If 'gas' is nil, the opcode GAS will // DelegateCall is a convenience function to make a delegatecall. If 'gas' is nil, the opcode GAS will
// be used to provide all gas. // be used to provide all gas.
func (p *Program) DelegateCall(gas *big.Int, address, inOffset, inSize, outOffset, outSize any) *Program { func (p *Program) DelegateCall(gas *uint256.Int, address, inOffset, inSize, outOffset, outSize any) *Program {
if outOffset == outSize && inSize == outSize && inOffset == outSize { if outOffset == outSize && inSize == outSize && inOffset == outSize {
p.Push(outSize).Ops(vm.DUP1, vm.DUP1, vm.DUP1) p.Push(outSize).Op(vm.DUP1, vm.DUP1, vm.DUP1)
} else { } else {
p.Push(outSize).Push(outOffset).Push(inSize).Push(inOffset) p.Push(outSize).Push(outOffset).Push(inSize).Push(inOffset)
} }
@ -175,16 +182,16 @@ func (p *Program) DelegateCall(gas *big.Int, address, inOffset, inSize, outOffse
if gas == nil { if gas == nil {
p.Op(vm.GAS) p.Op(vm.GAS)
} else { } else {
p.pushBig(gas) p.doPush(gas)
} }
return p.Op(vm.DELEGATECALL) return p.Op(vm.DELEGATECALL)
} }
// StaticCall is a convenience function to make a staticcall. If 'gas' is nil, the opcode GAS will // StaticCall is a convenience function to make a staticcall. If 'gas' is nil, the opcode GAS will
// be used to provide all gas. // be used to provide all gas.
func (p *Program) StaticCall(gas *big.Int, address, inOffset, inSize, outOffset, outSize any) *Program { func (p *Program) StaticCall(gas *uint256.Int, address, inOffset, inSize, outOffset, outSize any) *Program {
if outOffset == outSize && inSize == outSize && inOffset == outSize { if outOffset == outSize && inSize == outSize && inOffset == outSize {
p.Push(outSize).Ops(vm.DUP1, vm.DUP1, vm.DUP1) p.Push(outSize).Op(vm.DUP1, vm.DUP1, vm.DUP1)
} else { } else {
p.Push(outSize).Push(outOffset).Push(inSize).Push(inOffset) p.Push(outSize).Push(outOffset).Push(inSize).Push(inOffset)
} }
@ -192,16 +199,16 @@ func (p *Program) StaticCall(gas *big.Int, address, inOffset, inSize, outOffset,
if gas == nil { if gas == nil {
p.Op(vm.GAS) p.Op(vm.GAS)
} else { } else {
p.pushBig(gas) p.doPush(gas)
} }
return p.Op(vm.STATICCALL) return p.Op(vm.STATICCALL)
} }
// StaticCall is a convenience function to make a callcode. If 'gas' is nil, the opcode GAS will // StaticCall is a convenience function to make a callcode. If 'gas' is nil, the opcode GAS will
// be used to provide all gas. // be used to provide all gas.
func (p *Program) CallCode(gas *big.Int, address, value, inOffset, inSize, outOffset, outSize any) *Program { func (p *Program) CallCode(gas *uint256.Int, address, value, inOffset, inSize, outOffset, outSize any) *Program {
if outOffset == outSize && inSize == outSize && inOffset == outSize { if outOffset == outSize && inSize == outSize && inOffset == outSize {
p.Push(outSize).Ops(vm.DUP1, vm.DUP1, vm.DUP1) p.Push(outSize).Op(vm.DUP1, vm.DUP1, vm.DUP1)
} else { } else {
p.Push(outSize).Push(outOffset).Push(inSize).Push(inOffset) p.Push(outSize).Push(outOffset).Push(inSize).Push(inOffset)
} }
@ -210,54 +217,53 @@ func (p *Program) CallCode(gas *big.Int, address, value, inOffset, inSize, outOf
if gas == nil { if gas == nil {
p.Op(vm.GAS) p.Op(vm.GAS)
} else { } else {
p.pushBig(gas) p.doPush(gas)
} }
return p.Op(vm.CALLCODE) return p.Op(vm.CALLCODE)
} }
// Label returns the PC (of the next instruction) // Label returns the PC (of the next instruction).
func (p *Program) Label() uint64 { func (p *Program) Label() uint64 {
return uint64(len(p.code)) return uint64(len(p.code))
} }
// Jumpdest adds a JUMPDEST op, and returns the PC of that instruction // Jumpdest adds a JUMPDEST op, and returns the PC of that instruction.
func (p *Program) Jumpdest() (*Program, uint64) { func (p *Program) Jumpdest() (*Program, uint64) {
here := p.Label() here := p.Label()
p.Op(vm.JUMPDEST) p.Op(vm.JUMPDEST)
return p, here return p, here
} }
// Jump pushes the destination and adds a JUMP // Jump pushes the destination and adds a JUMP.
func (p *Program) Jump(loc any) *Program { func (p *Program) Jump(loc any) *Program {
p.Push(loc) p.Push(loc)
p.Op(vm.JUMP) p.Op(vm.JUMP)
return p return p
} }
// Jump pushes the destination and adds a JUMP // JumpIf implements JUMPI.
func (p *Program) JumpIf(loc any, condition any) { func (p *Program) JumpIf(loc any, condition any) *Program {
p.Push(condition) p.Push(condition)
p.Push(loc) p.Push(loc)
p.Op(vm.JUMPI) p.Op(vm.JUMPI)
return p
} }
// Size returns the current size of the bytecode.
func (p *Program) Size() int { func (p *Program) Size() int {
return len(p.code) return len(p.code)
} }
// InputToMemory stores the input (calldata) to memory as address (20 bytes). // InputAddressToStack stores the input (calldata) to memory as address (20 bytes).
func (p *Program) InputAddressToStack(inputOffset uint32) *Program { func (p *Program) InputAddressToStack(inputOffset uint32) *Program {
p.Push(inputOffset) p.Push(inputOffset)
p.Op(vm.CALLDATALOAD) // Loads [n -> n + 32] of input data to stack top p.Op(vm.CALLDATALOAD) // Loads [n -> n + 32] of input data to stack top
mask, ok := big.NewInt(0).SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 16) mask, _ := big.NewInt(0).SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 16)
if !ok {
panic("whoa")
}
p.Push(mask) // turn into address p.Push(mask) // turn into address
return p.Op(vm.AND) return p.Op(vm.AND)
} }
// MStore stores the provided data (into the memory area starting at memStart) // MStore stores the provided data (into the memory area starting at memStart).
func (p *Program) Mstore(data []byte, memStart uint32) *Program { func (p *Program) Mstore(data []byte, memStart uint32) *Program {
var idx = 0 var idx = 0
// We need to store it in chunks of 32 bytes // We need to store it in chunks of 32 bytes
@ -292,11 +298,11 @@ func (p *Program) Mstore(data []byte, memStart uint32) *Program {
func (p *Program) MstoreSmall(data []byte, memStart uint32) *Program { func (p *Program) MstoreSmall(data []byte, memStart uint32) *Program {
if len(data) > 32 { if len(data) > 32 {
// For larger sizes, use Mstore instead. // For larger sizes, use Mstore instead.
panic(fmt.Sprintf("only <=32 byte data size supported")) panic("only <=32 byte data size supported")
} }
if len(data) == 0 { if len(data) == 0 {
// Storing 0-length data smells of an error somewhere. // Storing 0-length data smells of an error somewhere.
panic(fmt.Sprintf("data is zero length")) panic("data is zero length")
} }
// push the value // push the value
p.Push(data) p.Push(data)
@ -309,7 +315,7 @@ func (p *Program) MstoreSmall(data []byte, memStart uint32) *Program {
// MemToStorage copies the given memory area into SSTORE slots, // MemToStorage copies the given memory area into SSTORE slots,
// It expects data to be aligned to 32 byte, and does not zero out // It expects data to be aligned to 32 byte, and does not zero out
// remainders if some data is not // remainders if some data is not
// I.e, if given a 1-byte area, it will still copy the full 32 bytes to storage // I.e, if given a 1-byte area, it will still copy the full 32 bytes to storage.
func (p *Program) MemToStorage(memStart, memSize, startSlot int) *Program { func (p *Program) MemToStorage(memStart, memSize, startSlot int) *Program {
// We need to store it in chunks of 32 bytes // We need to store it in chunks of 32 bytes
for idx := memStart; idx < (memStart + memSize); idx += 32 { for idx := memStart; idx < (memStart + memSize); idx += 32 {
@ -350,8 +356,8 @@ func (p *Program) ReturnViaCodeCopy(data []byte) *Program {
} }
// Sstore stores the given byte array to the given slot. // Sstore stores the given byte array to the given slot.
// OBS! Does not verify that the value indeed fits into 32 bytes // OBS! Does not verify that the value indeed fits into 32 bytes.
// If it does not, it will panic later on via pushBig // If it does not, it will panic later on via doPush.
func (p *Program) Sstore(slot any, value any) *Program { func (p *Program) Sstore(slot any, value any) *Program {
p.Push(value) p.Push(value)
p.Push(slot) p.Push(slot)
@ -359,14 +365,15 @@ func (p *Program) Sstore(slot any, value any) *Program {
} }
// Tstore stores the given byte array to the given t-slot. // Tstore stores the given byte array to the given t-slot.
// OBS! Does not verify that the value indeed fits into 32 bytes // OBS! Does not verify that the value indeed fits into 32 bytes.
// If it does not, it will panic later on via pushBig // If it does not, it will panic later on via doPush.
func (p *Program) Tstore(slot any, value any) *Program { func (p *Program) Tstore(slot any, value any) *Program {
p.Push(value) p.Push(value)
p.Push(slot) p.Push(slot)
return p.Op(vm.TSTORE) return p.Op(vm.TSTORE)
} }
// Return implements RETURN
func (p *Program) Return(offset, len int) *Program { func (p *Program) Return(offset, len int) *Program {
p.Push(len) p.Push(len)
p.Push(offset) p.Push(offset)
@ -396,13 +403,13 @@ func (p *Program) Create2(code []byte, salt any) *Program {
Push(value). Push(value).
Op(vm.CREATE2) Op(vm.CREATE2)
// On the stack now, is either // On the stack now, is either
// zero: in case of failure // - zero: in case of failure, OR
// address: in case of success // - address: in case of success
} }
// Create2AndCall calls create2 with the given initcode and salt, and then calls // Create2ThenCall calls create2 with the given initcode and salt, and then calls
// into the created contract (or calls into zero, if the creation failed). // into the created contract (or calls into zero, if the creation failed).
func (p *Program) Create2AndCall(code []byte, salt any) *Program { func (p *Program) Create2ThenCall(code []byte, salt any) *Program {
p.Create2(code, salt) p.Create2(code, salt)
// If there happen to be a zero on the stack, it doesn't matter, we're // If there happen to be a zero on the stack, it doesn't matter, we're
// not sending any value anyway // not sending any value anyway

@ -23,6 +23,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/holiman/uint256"
) )
func TestPush(t *testing.T) { func TestPush(t *testing.T) {
@ -32,13 +33,19 @@ func TestPush(t *testing.T) {
}{ }{
// native ints // native ints
{0, "6000"}, {0, "6000"},
{0xfff, "610fff"},
{nil, "6000"}, {nil, "6000"},
{uint8(1), "6001"},
{uint16(1), "6001"},
{uint32(1), "6001"},
{uint64(1), "6001"}, {uint64(1), "6001"},
{0xfff, "610fff"},
// bigints // bigints
{big.NewInt(0), "6000"}, {big.NewInt(0), "6000"},
{big.NewInt(1), "6001"}, {big.NewInt(1), "6001"},
{big.NewInt(0xfff), "610fff"}, {big.NewInt(0xfff), "610fff"},
// uint256
{uint256.NewInt(1), "6001"},
{uint256.Int{1, 0, 0, 0}, "6001"},
// Addresses // Addresses
{common.HexToAddress("0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"), "73deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"}, {common.HexToAddress("0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"), "73deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"},
{&common.Address{}, "6000"}, {&common.Address{}, "6000"},
@ -60,7 +67,7 @@ func TestCall(t *testing.T) {
} }
} }
{ // Non nil gas { // Non nil gas
have := New().Call(big.NewInt(0xffff), common.HexToAddress("0x1337"), big.NewInt(1), 1, 2, 3, 4).Hex() have := New().Call(uint256.NewInt(0xffff), common.HexToAddress("0x1337"), big.NewInt(1), 1, 2, 3, 4).Hex()
want := "6004600360026001600161133761fffff1" want := "6004600360026001600161133761fffff1"
if have != want { if have != want {
t.Errorf("have %v want %v", have, want) t.Errorf("have %v want %v", have, want)
@ -171,7 +178,7 @@ func TestCreateAndCall(t *testing.T) {
func TestCreate2Call(t *testing.T) { func TestCreate2Call(t *testing.T) {
// Some runtime code // Some runtime code
runtime := New().Ops(vm.ADDRESS, vm.SELFDESTRUCT).Bytes() runtime := New().Op(vm.ADDRESS, vm.SELFDESTRUCT).Bytes()
want := common.FromHex("0x30ff") want := common.FromHex("0x30ff")
if !bytes.Equal(want, runtime) { if !bytes.Equal(want, runtime) {
t.Fatalf("runtime code error\nwant: %x\nhave: %x\n", want, runtime) t.Fatalf("runtime code error\nwant: %x\nhave: %x\n", want, runtime)
@ -183,7 +190,7 @@ func TestCreate2Call(t *testing.T) {
t.Fatalf("initcode error\nwant: %x\nhave: %x\n", want, initcode) t.Fatalf("initcode error\nwant: %x\nhave: %x\n", want, initcode)
} }
// A factory invoking the constructor // A factory invoking the constructor
outer := New().Create2AndCall(initcode, nil).Bytes() outer := New().Create2ThenCall(initcode, nil).Bytes()
want = common.FromHex("60606000536030600153606060025360006003536053600453606060055360ff6006536060600753600160085360536009536060600a536002600b536060600c536000600d5360f3600e536000600f60006000f560006000600060006000855af15050") want = common.FromHex("60606000536030600153606060025360006003536053600453606060055360ff6006536060600753600160085360536009536060600a536002600b536060600c536000600d5360f3600e536000600f60006000f560006000600060006000855af15050")
if !bytes.Equal(want, outer) { if !bytes.Equal(want, outer) {
t.Fatalf("factory error\nwant: %x\nhave: %x\n", want, outer) t.Fatalf("factory error\nwant: %x\nhave: %x\n", want, outer)
@ -211,9 +218,9 @@ func TestGenerator(t *testing.T) {
haveFn: func() []byte { haveFn: func() []byte {
initcode := New().Return(0, 0).Bytes() initcode := New().Return(0, 0).Bytes()
return New().MstoreSmall(initcode, 0). return New().MstoreSmall(initcode, 0).
Push(len(initcode)). // length Push(len(initcode)). // length
Push(32 - len(initcode)). // offset Push(32 - len(initcode)). // offset
Push(0). // value Push(0). // value
Op(vm.CREATE). Op(vm.CREATE).
Op(vm.POP).Bytes() Op(vm.POP).Bytes()
}, },
@ -234,10 +241,10 @@ func TestGenerator(t *testing.T) {
haveFn: func() []byte { haveFn: func() []byte {
initcode := New().Return(0, 0).Bytes() initcode := New().Return(0, 0).Bytes()
return New().MstoreSmall(initcode, 0). return New().MstoreSmall(initcode, 0).
Push(1). // salt Push(1). // salt
Push(len(initcode)). // length Push(len(initcode)). // length
Push(32 - len(initcode)). // offset Push(32 - len(initcode)). // offset
Push(0). // value Push(0). // value
Op(vm.CREATE2). Op(vm.CREATE2).
Op(vm.POP).Bytes() Op(vm.POP).Bytes()
}, },

@ -437,7 +437,6 @@ func benchmarkNonModifyingCode(gas uint64, code []byte, name string, tracerCode
// BenchmarkSimpleLoop test a pretty simple loop which loops until OOG // BenchmarkSimpleLoop test a pretty simple loop which loops until OOG
// 55 ms // 55 ms
func BenchmarkSimpleLoop(b *testing.B) { func BenchmarkSimpleLoop(b *testing.B) {
p, lbl := program.New().Jumpdest() p, lbl := program.New().Jumpdest()
// Call identity, and pop return value // Call identity, and pop return value
staticCallIdentity := p. staticCallIdentity := p.
@ -457,22 +456,22 @@ func BenchmarkSimpleLoop(b *testing.B) {
p, lbl = program.New().Jumpdest() p, lbl = program.New().Jumpdest()
callEOA := p. callEOA := p.
Call(nil, 0xE0, 0, 0, 0, 0, 0). // call addr of EOA Call(nil, 0xE0, 0, 0, 0, 0, 0). // call addr of EOA
Op(vm.POP).Jump(lbl).Bytes() // pop return value and jump to label Op(vm.POP).Jump(lbl).Bytes() // pop return value and jump to label
p, lbl = program.New().Jumpdest() p, lbl = program.New().Jumpdest()
// Push as if we were making call, then pop it off again, and loop // Push as if we were making call, then pop it off again, and loop
loopingCode := p.Push(0). loopingCode := p.Push(0).
Ops(vm.DUP1, vm.DUP1, vm.DUP1). Op(vm.DUP1, vm.DUP1, vm.DUP1).
Push(0x4). Push(0x4).
Ops(vm.GAS, vm.POP, vm.POP, vm.POP, vm.POP, vm.POP, vm.POP). Op(vm.GAS, vm.POP, vm.POP, vm.POP, vm.POP, vm.POP, vm.POP).
Jump(lbl).Bytes() Jump(lbl).Bytes()
p, lbl = program.New().Jumpdest() p, lbl = program.New().Jumpdest()
loopingCode2 := p. loopingCode2 := p.
Push(0x01020304).Push(uint64(0x0102030405)). Push(0x01020304).Push(uint64(0x0102030405)).
Ops(vm.POP, vm.POP). Op(vm.POP, vm.POP).
Op(vm.PUSH6).Append(make([]byte, 6)).Op(vm.JUMP). // Jumpdest zero expressed in 6 bytes Op(vm.PUSH6).Append(make([]byte, 6)).Op(vm.JUMP). // Jumpdest zero expressed in 6 bytes
Bytes() Jump(lbl).Bytes()
p, lbl = program.New().Jumpdest() p, lbl = program.New().Jumpdest()
callRevertingContractWithInput := p. callRevertingContractWithInput := p.
@ -723,20 +722,20 @@ func TestRuntimeJSTracer(t *testing.T) {
results []string results []string
}{ }{
{ // CREATE { // CREATE
code: program.New().MstorePadded(initcode, 0). code: program.New().MstoreSmall(initcode, 0).
Push(len(initcode)). // length Push(len(initcode)). // length
Push(32 - len(initcode)). // offset Push(32 - len(initcode)). // offset
Push(0). // value Push(0). // value
Op(vm.CREATE). Op(vm.CREATE).
Op(vm.POP).Bytes(), Op(vm.POP).Bytes(),
results: []string{`"1,1,952853,6,12"`, `"1,1,952853,6,0"`}, results: []string{`"1,1,952853,6,12"`, `"1,1,952853,6,0"`},
}, },
{ // CREATE2 { // CREATE2
code: program.New().MstorePadded(initcode, 0). code: program.New().MstoreSmall(initcode, 0).
Push(1). // salt Push(1). // salt
Push(len(initcode)). // length Push(len(initcode)). // length
Push(32 - len(initcode)). // offset Push(32 - len(initcode)). // offset
Push(0). // value Push(0). // value
Op(vm.CREATE2). Op(vm.CREATE2).
Op(vm.POP).Bytes(), Op(vm.POP).Bytes(),
results: []string{`"1,1,952844,6,13"`, `"1,1,952844,6,0"`}, results: []string{`"1,1,952844,6,13"`, `"1,1,952844,6,0"`},
@ -842,7 +841,7 @@ func TestJSTracerCreateTx(t *testing.T) {
func BenchmarkTracerStepVsCallFrame(b *testing.B) { func BenchmarkTracerStepVsCallFrame(b *testing.B) {
// Simply pushes and pops some values in a loop // Simply pushes and pops some values in a loop
p, lbl := program.New().Jumpdest() p, lbl := program.New().Jumpdest()
code := p.Push(0).Push(0).Ops(vm.POP, vm.POP).Jump(lbl).Bytes() code := p.Push(0).Push(0).Op(vm.POP, vm.POP).Jump(lbl).Bytes()
stepTracer := ` stepTracer := `
{ {
step: function() {}, step: function() {},

Loading…
Cancel
Save