diff --git a/binary/integer.go b/binary/integer.go index f865148400b866d4b071f469c934d1fb297c2f7b..a00c07054502952f50408eba9a4bf8d000c654ba 100644 --- a/binary/integer.go +++ b/binary/integer.go @@ -17,10 +17,14 @@ package binary import ( "encoding/binary" "math" + "math/big" "sort" ) -const Uint64TopBitMask = 1 << 63 +var big1 = big.NewInt(1) +var tt256 = new(big.Int).Lsh(big1, 256) +var tt256m1 = new(big.Int).Sub(new(big.Int).Lsh(big1, 256), big1) +var tt255 = new(big.Int).Lsh(big1, 255) // Sort for []uint64 @@ -75,3 +79,42 @@ func GetInt64BE(src []byte) int64 { func IsUint64SumOverflow(a, b uint64) bool { return math.MaxUint64-a < b } + +// + +// Converts a possibly negative big int x into a positive big int encoding a twos complement representation of x +// truncated to 32 bytes +func U256(x *big.Int) *big.Int { + // Note that the And operation induces big.Int to hold a positive representation of a negative number + return new(big.Int).And(x, tt256m1) +} + +// Interprets a positive big.Int as a 256-bit two's complement signed integer +func S256(x *big.Int) *big.Int { + // Sign bit not set, value is its positive self + if x.Cmp(tt255) < 0 { + return x + } else { + // negative value is represented + return new(big.Int).Sub(x, tt256) + } +} + +// Treats the positive big int x as if it contains an embedded a back + 1 byte signed integer in its least significant +// bits and extends that sign +func SignExtend(back uint64, x *big.Int) *big.Int { + // we assume x contains a signed integer of back + 1 bytes width + // most significant bit of the back'th byte, + signBit := back*8 + 7 + // single bit set at sign bit position + mask := new(big.Int).Lsh(big1, uint(signBit)) + // all bits below sign bit set to 1 all above (including sign bit) set to 0 + mask.Sub(mask, big1) + if x.Bit(int(signBit)) == 1 { + // Number represented is negative - set all bits above sign bit (including sign bit) + return x.Or(x, mask.Not(mask)) + } else { + // Number represented is positive - clear all bits above sign bit (including sign bit) + return x.And(x, mask) + } +} diff --git a/binary/integer_test.go b/binary/integer_test.go index e530c5a7bb6b312fb2e8777796889b1629cb4a45..c0c1533fecac2e81eb73a754eab919e6c505af04 100644 --- a/binary/integer_test.go +++ b/binary/integer_test.go @@ -2,9 +2,16 @@ package binary import ( "math" + "math/big" "testing" + "strconv" + "strings" + + "fmt" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestIsUint64SumOverflow(t *testing.T) { @@ -17,3 +24,154 @@ func TestIsUint64SumOverflow(t *testing.T) { assert.True(t, IsUint64SumOverflow(a+b, 1)) assert.True(t, IsUint64SumOverflow(a+1, b+1)) } + +func zero() *big.Int { + return new(big.Int) +} + +var big2E255 = zero().Lsh(big1, 255) +var big2E256 = zero().Lsh(big1, 256) +var big2E257 = zero().Lsh(big1, 257) + +func TestU256(t *testing.T) { + expected := big2E255 + encoded := U256(expected) + assertBigIntEqual(t, expected, encoded, "Top bit set big int is fixed point") + + expected = zero() + encoded = U256(big2E256) + assertBigIntEqual(t, expected, encoded, "Ceiling bit is exact overflow") + + expected = zero().Sub(big2E256, big1) + encoded = U256(expected) + assertBigIntEqual(t, expected, encoded, "Max unsigned big int is fixed point") + + expected = big1 + encoded = U256(zero().Add(big2E256, big1)) + assertBigIntEqual(t, expected, encoded, "Overflow by one") + + expected = big2E255 + encoded = U256(zero().Add(big2E256, big2E255)) + assertBigIntEqual(t, expected, encoded, "Overflow by doubling") + + negative := big.NewInt(-234) + assert.Equal(t, "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF16", + fmt.Sprintf("%X", U256(negative).Bytes()), "byte representation is twos complement") + + expected, ok := zero().SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF16", 16) + require.True(t, ok) + assertBigIntEqual(t, expected, U256(negative), "Bytes representation should be twos complement") + + expected = zero() + encoded = U256(zero().Neg(big2E256)) + assertBigIntEqual(t, expected, encoded, "Floor bit is overflow") + + expected = big2E255 + encoded = zero().Neg(big2E255) + encoded = U256(encoded) + assertBigIntEqual(t, expected, encoded, "2**255 is Self complement") + + expected = zero().Add(big2E255, big1) + encoded = zero().Neg(big2E255) + encoded = encoded.Add(encoded, big1) + encoded = U256(encoded) + assertBigIntEqual(t, expected, encoded, "") +} + +func TestS256(t *testing.T) { + expected := zero().Neg(big2E255) + signed := S256(big2E255) + assertBigIntEqual(t, expected, signed, "Should be negative") + + expected = zero().Sub(big2E255, big1) + signed = S256(expected) + assertBigIntEqual(t, expected, signed, "Maximum twos complement positive is fixed point") + + expected = zero() + signed = S256(expected) + assertBigIntEqual(t, expected, signed, "Twos complement of zero is fixed poount") + + // Technically undefined but let's not let that stop us + expected = zero().Sub(big2E257, big2E256) + signed = S256(big2E257) + assertBigIntEqual(t, expected, signed, "Out of twos complement bounds") +} + +func TestSignExtend(t *testing.T) { + assertSignExtend(t, 16, 0, + "0000 0000 1001 0000", + "1111 1111 1001 0000") + + assertSignExtend(t, 16, 1, + "1001 0000", + "1001 0000") + + assertSignExtend(t, 32, 2, + "0000 0000 1000 0000 1101 0011 1001 0000", + "1111 1111 1000 0000 1101 0011 1001 0000") + + assertSignExtend(t, 32, 2, + "0000 0000 0000 0000 1101 0011 1001 0000", + "0000 0000 0000 0000 1101 0011 1001 0000") + + // Here we have a stray bit set in the 4th most significant byte that gets wiped out + assertSignExtend(t, 32, 2, + "0001 0000 0000 0000 1101 0011 1001 0000", + "0000 0000 0000 0000 1101 0011 1001 0000") + assertSignExtend(t, 32, 2, + "0001 0000 1000 0000 1101 0011 1001 0000", + "1111 1111 1000 0000 1101 0011 1001 0000") + + assertSignExtend(t, 32, 3, + "0001 0000 1000 0000 1101 0011 1001 0000", + "0001 0000 1000 0000 1101 0011 1001 0000") + + assertSignExtend(t, 32, 3, + "1001 0000 1000 0000 1101 0011 1001 0000", + "1001 0000 1000 0000 1101 0011 1001 0000") + + assertSignExtend(t, 64, 3, + "0000 0000 0000 0000 0000 0000 0000 0000 1001 0000 1000 0000 1101 0011 1001 0000", + "1111 1111 1111 1111 1111 1111 1111 1111 1001 0000 1000 0000 1101 0011 1001 0000") + + assertSignExtend(t, 64, 3, + "0000 0000 0000 0000 0000 0000 0000 0000 0001 0000 1000 0000 1101 0011 1001 0000", + "0000 0000 0000 0000 0000 0000 0000 0000 0001 0000 1000 0000 1101 0011 1001 0000") +} + +func assertSignExtend(t *testing.T, bitSize int, bytesBack uint64, inputString, expectedString string) bool { + input := intFromString(t, bitSize, inputString) + expected := intFromString(t, bitSize, expectedString) + //actual := SignExtend(big.NewInt(bytesBack), big.NewInt(int64(input))) + actual := SignExtend(bytesBack, big.NewInt(int64(input))) + var ret bool + switch bitSize { + case 8: + ret = assert.Equal(t, uint8(expected), uint8(actual.Int64())) + case 16: + ret = assert.Equal(t, uint16(expected), uint16(actual.Int64())) + case 32: + ret = assert.Equal(t, uint32(expected), uint32(actual.Int64())) + case 64: + ret = assert.Equal(t, uint64(expected), uint64(actual.Int64())) + default: + t.Fatalf("Cannot test SignExtend for non-Go-native bit size %v", bitSize) + return false + } + if !ret { + + } + return ret +} + +func assertBigIntEqual(t *testing.T, expected, actual *big.Int, messages ...string) bool { + return assert.True(t, expected.Cmp(actual) == 0, fmt.Sprintf("%s - not equal:\n%v (expected)\n%v (actual)", + strings.Join(messages, " "), expected, actual)) +} + +func intFromString(t *testing.T, bitSize int, binStrings ...string) uint64 { + binaryString := strings.Replace(strings.Join(binStrings, ""), " ", "", -1) + i, err := strconv.ParseUint(binaryString, 2, bitSize) + require.NoError(t, err) + return i +} diff --git a/binary/word256.go b/binary/word256.go index 6d11fe9aa27538f0ea21b4f94b8183edcfb4f13b..02a90581f9c297f49c9fe08369755efa82694b3f 100644 --- a/binary/word256.go +++ b/binary/word256.go @@ -16,16 +16,19 @@ package binary import ( "bytes" + "math/big" "sort" ) var ( Zero256 = Word256{} - One256 = Word256{1} + One256 = LeftPadWord256([]byte{1}) ) const Word256Length = 32 +var BigWord256Length = big.NewInt(Word256Length) + var trimCutSet = string([]byte{0}) type Word256 [Word256Length]byte diff --git a/binary/word256_test.go b/binary/word256_test.go index 055d39109e6e3cdbc249948b2071837ba3a9c87d..78aaeb596829d9d7f9a073fd5402d2e067a4ab74 100644 --- a/binary/word256_test.go +++ b/binary/word256_test.go @@ -37,3 +37,7 @@ func TestLeftPadWord256(t *testing.T) { }, LeftPadWord256([]byte{1, 2, 3})) } + +func TestOne256(t *testing.T) { + assert.Equal(t, Int64ToWord256(1), One256) +} diff --git a/execution/evm/common.go b/execution/evm/common.go deleted file mode 100644 index e8fb7444c8afcfa69e387323a654c4c2bb5f90ae..0000000000000000000000000000000000000000 --- a/execution/evm/common.go +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2017 Monax Industries Limited -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package evm - -import ( - "math/big" -) - -// To256 -// -// "cast" the big int to a 256 big int (i.e., limit to) -var tt256 = new(big.Int).Lsh(big.NewInt(1), 256) -var tt256m1 = new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 256), big.NewInt(1)) -var tt255 = new(big.Int).Lsh(big.NewInt(1), 255) - -func U256(x *big.Int) *big.Int { - x.And(x, tt256m1) - return x -} - -func S256(x *big.Int) *big.Int { - if x.Cmp(tt255) < 0 { - return x - } else { - // We don't want to modify x, ever - return new(big.Int).Sub(x, tt256) - } -} diff --git a/execution/evm/memory.go b/execution/evm/memory.go index 6eb8456cbbd6e861ba56993a8819622e178bd3da..126686146fcee28cb9ae5e964296daf874dbc211 100644 --- a/execution/evm/memory.go +++ b/execution/evm/memory.go @@ -3,6 +3,7 @@ package evm import ( "fmt" "math" + "math/big" ) const ( @@ -16,7 +17,7 @@ const ( // unlikely to make a lot of difference. var zeroBlock []byte = make([]byte, 32) -// Interface for a bounded linear memory indexed by a single int64 parameter +// Interface for a bounded linear memory indexed by a single *big.Int parameter // for each byte in the memory. type Memory interface { // Read a value from the memory store starting at offset @@ -26,20 +27,23 @@ type Memory interface { // // The value returned should be copy of any underlying memory, not a reference // to the underlying store. - Read(offset, length int64) ([]byte, error) + Read(offset, length *big.Int) ([]byte, error) // Write a value to the memory starting at offset (the index of the first byte // written will equal offset). The value is provided as bytes to be written // consecutively to the memory store. Return an error if the memory cannot be // written or allocated. - Write(offset int64, value []byte) error + Write(offset *big.Int, value []byte) error // Returns the current capacity of the memory. For dynamically allocating // memory this capacity can be used as a write offset that is guaranteed to be // unused. Solidity in particular makes this assumption when using MSIZE to // get the current allocated memory. - Capacity() int64 + Capacity() *big.Int } -func NewDynamicMemory(initialCapacity, maximumCapacity int64) Memory { +// Get a new DynamicMemory (note that although we take a maximumCapacity of uint64 we currently +// limit the maximum to int32 at runtime because we are using a single slice which we cannot guarantee +// to be indexable above int32 or all validators +func NewDynamicMemory(initialCapacity, maximumCapacity uint64) Memory { return &dynamicMemory{ slice: make([]byte, initialCapacity), maximumCapacity: maximumCapacity, @@ -54,10 +58,22 @@ func DefaultDynamicMemoryProvider() Memory { // array allocation via a backing slice type dynamicMemory struct { slice []byte - maximumCapacity int64 + maximumCapacity uint64 } -func (mem *dynamicMemory) Read(offset, length int64) ([]byte, error) { +func (mem *dynamicMemory) Read(offset, length *big.Int) ([]byte, error) { + // Ensures positive and not too wide + if !offset.IsUint64() { + return nil, fmt.Errorf("offset %v does not fit inside an unsigned 64-bit integer", offset) + } + // Ensures positive and not too wide + if !length.IsUint64() { + return nil, fmt.Errorf("length %v does not fit inside an unsigned 64-bit integer", offset) + } + return mem.read(offset.Uint64(), length.Uint64()) +} + +func (mem *dynamicMemory) read(offset, length uint64) ([]byte, error) { capacity := offset + length err := mem.ensureCapacity(capacity) if err != nil { @@ -68,8 +84,16 @@ func (mem *dynamicMemory) Read(offset, length int64) ([]byte, error) { return value, nil } -func (mem *dynamicMemory) Write(offset int64, value []byte) error { - capacity := offset + int64(len(value)) +func (mem *dynamicMemory) Write(offset *big.Int, value []byte) error { + // Ensures positive and not too wide + if !offset.IsUint64() { + return fmt.Errorf("offset %v does not fit inside an unsigned 64-bit integer", offset) + } + return mem.write(offset.Uint64(), value) +} + +func (mem *dynamicMemory) write(offset uint64, value []byte) error { + capacity := offset + uint64(len(value)) err := mem.ensureCapacity(capacity) if err != nil { return err @@ -78,18 +102,21 @@ func (mem *dynamicMemory) Write(offset int64, value []byte) error { return nil } -func (mem *dynamicMemory) Capacity() int64 { - return int64(len(mem.slice)) +func (mem *dynamicMemory) Capacity() *big.Int { + return big.NewInt(int64(len(mem.slice))) } // Ensures the current memory store can hold newCapacity. Will only grow the // memory (will not shrink). -func (mem *dynamicMemory) ensureCapacity(newCapacity int64) error { +func (mem *dynamicMemory) ensureCapacity(newCapacity uint64) error { + // Maximum length of a slice that allocates memory is the same as the native int max size + // We could rethink this limit, but we don't want different validators to disagree on + // transaction validity so we pick the lowest common denominator if newCapacity > math.MaxInt32 { // If we ever did want more than an int32 of space then we would need to // maintain multiple pages of memory return fmt.Errorf("cannot address memory beyond a maximum index "+ - "of Int32 type (%v bytes)", math.MaxInt32) + "with int32 width (%v bytes)", math.MaxInt32) } newCapacityInt := int(newCapacity) // We're already big enough so return diff --git a/execution/evm/memory_test.go b/execution/evm/memory_test.go index 923cdda45d6a83c5532f523d3c360e655457ab55..c60a858339f25a2874b75c5751e14fb48e5cd7e4 100644 --- a/execution/evm/memory_test.go +++ b/execution/evm/memory_test.go @@ -3,14 +3,16 @@ package evm import ( "testing" + "math/big" + "github.com/stretchr/testify/assert" ) // Test static memory allocation with maximum == initial capacity - memory should not grow func TestDynamicMemory_StaticAllocation(t *testing.T) { mem := NewDynamicMemory(4, 4).(*dynamicMemory) - mem.Write(0, []byte{1}) - mem.Write(1, []byte{0, 0, 1}) + mem.Write(big.NewInt(0), []byte{1}) + mem.Write(big.NewInt(1), []byte{0, 0, 1}) assert.Equal(t, []byte{1, 0, 0, 1}, mem.slice) assert.Equal(t, 4, cap(mem.slice), "Slice capacity should not grow") } @@ -18,31 +20,31 @@ func TestDynamicMemory_StaticAllocation(t *testing.T) { // Test reading beyond the current capacity - memory should grow func TestDynamicMemory_ReadAhead(t *testing.T) { mem := NewDynamicMemory(4, 8).(*dynamicMemory) - value, err := mem.Read(2, 4) + value, err := mem.Read(big.NewInt(2), big.NewInt(4)) assert.NoError(t, err) // Value should be size requested assert.Equal(t, []byte{0, 0, 0, 0}, value) // Slice should have grown to that plus offset assert.Equal(t, []byte{0, 0, 0, 0, 0, 0}, mem.slice) - value, err = mem.Read(2, 6) + value, err = mem.Read(big.NewInt(2), big.NewInt(6)) assert.NoError(t, err) assert.Equal(t, []byte{0, 0, 0, 0, 0, 0}, value) assert.Equal(t, []byte{0, 0, 0, 0, 0, 0, 0, 0}, mem.slice) // Check cannot read out of bounds - _, err = mem.Read(2, 7) + _, err = mem.Read(big.NewInt(2), big.NewInt(7)) assert.Error(t, err) } // Test writing beyond the current capacity - memory should grow func TestDynamicMemory_WriteAhead(t *testing.T) { mem := NewDynamicMemory(4, 8).(*dynamicMemory) - err := mem.Write(4, []byte{1, 2, 3, 4}) + err := mem.Write(big.NewInt(4), []byte{1, 2, 3, 4}) assert.NoError(t, err) assert.Equal(t, []byte{0, 0, 0, 0, 1, 2, 3, 4}, mem.slice) - err = mem.Write(4, []byte{1, 2, 3, 4, 5}) + err = mem.Write(big.NewInt(4), []byte{1, 2, 3, 4, 5}) assert.Error(t, err) } @@ -56,21 +58,21 @@ to describe: his sensation of a few hours before on Grattan Bridge, for example. If he could get back again into that mood....`) // Write the bytes - offset := 0x1000000 - err := mem.Write(int64(offset), bytesToWrite) + offset := big.NewInt(0x1000000) + err := mem.Write(offset, bytesToWrite) assert.NoError(t, err) - assert.Equal(t, append(make([]byte, offset), bytesToWrite...), mem.slice) - assert.Equal(t, offset+len(bytesToWrite), len(mem.slice)) + assert.Equal(t, append(make([]byte, offset.Uint64()), bytesToWrite...), mem.slice) + assert.Equal(t, offset.Uint64()+uint64(len(bytesToWrite)), uint64(len(mem.slice))) // Read them back - value, err := mem.Read(int64(offset), int64(len(bytesToWrite))) + value, err := mem.Read(offset, big.NewInt(int64(len(bytesToWrite)))) assert.NoError(t, err) assert.Equal(t, bytesToWrite, value) } func TestDynamicMemory_ZeroInitialMemory(t *testing.T) { mem := NewDynamicMemory(0, 16).(*dynamicMemory) - err := mem.Write(4, []byte{1, 2, 3, 4}) + err := mem.Write(big.NewInt(4), []byte{1, 2, 3, 4}) assert.NoError(t, err) assert.Equal(t, []byte{0, 0, 0, 0, 1, 2, 3, 4}, mem.slice) } @@ -78,15 +80,15 @@ func TestDynamicMemory_ZeroInitialMemory(t *testing.T) { func TestDynamicMemory_Capacity(t *testing.T) { mem := NewDynamicMemory(1, 0x10000000).(*dynamicMemory) - assert.Equal(t, int64(1), mem.Capacity()) + assert.Equal(t, big.NewInt(1), mem.Capacity()) - capacity := int64(1234) - err := mem.ensureCapacity(capacity) + capacity := big.NewInt(1234) + err := mem.ensureCapacity(capacity.Uint64()) assert.NoError(t, err) assert.Equal(t, capacity, mem.Capacity()) - capacity = int64(123456789) - err = mem.ensureCapacity(capacity) + capacity = big.NewInt(123456789) + err = mem.ensureCapacity(capacity.Uint64()) assert.NoError(t, err) assert.Equal(t, capacity, mem.Capacity()) diff --git a/execution/evm/stack.go b/execution/evm/stack.go index 0d8a8f3fae56d01205a82656ec9d0670b1719cd5..2db8018355dab6635a48dcd246254d79780f01f5 100644 --- a/execution/evm/stack.go +++ b/execution/evm/stack.go @@ -17,6 +17,8 @@ package evm import ( "fmt" + "math/big" + . "github.com/hyperledger/burrow/binary" ) @@ -78,6 +80,15 @@ func (st *Stack) PushU64(i uint64) { st.Push(Uint64ToWord256(i)) } +// Pushes the bigInt as a Word256 encoding negative values in 32-byte twos complement and returns the encoded result +func (st *Stack) PushBigInt(bigInt *big.Int) Word256 { + word := LeftPadWord256(U256(bigInt).Bytes()) + st.Push(word) + return word +} + +// Pops + func (st *Stack) Pop() Word256 { st.useGas(GasStackOp) if st.ptr == 0 { @@ -102,6 +113,15 @@ func (st *Stack) PopU64() uint64 { return Uint64FromWord256(d) } +func (st *Stack) PopBigIntSigned() *big.Int { + return S256(st.PopBigInt()) +} + +func (st *Stack) PopBigInt() *big.Int { + d := st.Pop() + return new(big.Int).SetBytes(d[:]) +} + func (st *Stack) Len() int { return st.ptr } diff --git a/execution/evm/vm.go b/execution/evm/vm.go index f20efce78f68c56e32abba9b1650a67f81866b1b..fe1f51cbfde5783612babd698940888874609e0a 100644 --- a/execution/evm/vm.go +++ b/execution/evm/vm.go @@ -18,7 +18,6 @@ import ( "bytes" "errors" "fmt" - "math/big" acm "github.com/hyperledger/burrow/account" . "github.com/hyperledger/burrow/binary" @@ -220,202 +219,147 @@ func (vm *VM) call(caller, callee acm.MutableAccount, code, input []byte, value switch op { case ADD: // 0x01 - x, y := stack.Pop(), stack.Pop() - xb := new(big.Int).SetBytes(x[:]) - yb := new(big.Int).SetBytes(y[:]) - sum := new(big.Int).Add(xb, yb) - res := LeftPadWord256(U256(sum).Bytes()) - stack.Push(res) - vm.Debugf(" %v + %v = %v (%X)\n", xb, yb, sum, res) + x, y := stack.PopBigInt(), stack.PopBigInt() + sum := x.Add(x, y) + res := stack.PushBigInt(sum) + vm.Debugf(" %v + %v = %v (%X)\n", x, y, sum, res) case MUL: // 0x02 - x, y := stack.Pop(), stack.Pop() - xb := new(big.Int).SetBytes(x[:]) - yb := new(big.Int).SetBytes(y[:]) - prod := new(big.Int).Mul(xb, yb) - res := LeftPadWord256(U256(prod).Bytes()) - stack.Push(res) - vm.Debugf(" %v * %v = %v (%X)\n", xb, yb, prod, res) + x, y := stack.PopBigInt(), stack.PopBigInt() + prod := x.Mul(x, y) + res := stack.PushBigInt(prod) + vm.Debugf(" %v * %v = %v (%X)\n", x, y, prod, res) case SUB: // 0x03 - x, y := stack.Pop(), stack.Pop() - xb := new(big.Int).SetBytes(x[:]) - yb := new(big.Int).SetBytes(y[:]) - diff := new(big.Int).Sub(xb, yb) - res := LeftPadWord256(U256(diff).Bytes()) - stack.Push(res) - vm.Debugf(" %v - %v = %v (%X)\n", xb, yb, diff, res) + x, y := stack.PopBigInt(), stack.PopBigInt() + diff := x.Sub(x, y) + res := stack.PushBigInt(diff) + vm.Debugf(" %v - %v = %v (%X)\n", x, y, diff, res) case DIV: // 0x04 - x, y := stack.Pop(), stack.Pop() - if y.IsZero() { + x, y := stack.PopBigInt(), stack.PopBigInt() + if y.Sign() == 0 { stack.Push(Zero256) vm.Debugf(" %x / %x = %v\n", x, y, 0) } else { - xb := new(big.Int).SetBytes(x[:]) - yb := new(big.Int).SetBytes(y[:]) - div := new(big.Int).Div(xb, yb) - res := LeftPadWord256(U256(div).Bytes()) - stack.Push(res) - vm.Debugf(" %v / %v = %v (%X)\n", xb, yb, div, res) + div := x.Div(x, y) + res := stack.PushBigInt(div) + vm.Debugf(" %v / %v = %v (%X)\n", x, y, div, res) } case SDIV: // 0x05 - x, y := stack.Pop(), stack.Pop() - if y.IsZero() { + x, y := stack.PopBigIntSigned(), stack.PopBigIntSigned() + if y.Sign() == 0 { stack.Push(Zero256) vm.Debugf(" %x / %x = %v\n", x, y, 0) } else { - xb := S256(new(big.Int).SetBytes(x[:])) - yb := S256(new(big.Int).SetBytes(y[:])) - div := new(big.Int).Div(xb, yb) - res := LeftPadWord256(U256(div).Bytes()) - stack.Push(res) - vm.Debugf(" %v / %v = %v (%X)\n", xb, yb, div, res) + div := x.Div(x, y) + res := stack.PushBigInt(div) + vm.Debugf(" %v / %v = %v (%X)\n", x, y, div, res) } case MOD: // 0x06 - x, y := stack.Pop(), stack.Pop() - if y.IsZero() { + x, y := stack.PopBigInt(), stack.PopBigInt() + if y.Sign() == 0 { stack.Push(Zero256) vm.Debugf(" %v %% %v = %v\n", x, y, 0) } else { - xb := new(big.Int).SetBytes(x[:]) - yb := new(big.Int).SetBytes(y[:]) - mod := new(big.Int).Mod(xb, yb) - res := LeftPadWord256(U256(mod).Bytes()) - stack.Push(res) - vm.Debugf(" %v %% %v = %v (%X)\n", xb, yb, mod, res) + mod := x.Mod(x, y) + res := stack.PushBigInt(mod) + vm.Debugf(" %v %% %v = %v (%X)\n", x, y, mod, res) } case SMOD: // 0x07 - x, y := stack.Pop(), stack.Pop() - if y.IsZero() { + x, y := stack.PopBigIntSigned(), stack.PopBigIntSigned() + if y.Sign() == 0 { stack.Push(Zero256) vm.Debugf(" %v %% %v = %v\n", x, y, 0) } else { - xb := S256(new(big.Int).SetBytes(x[:])) - yb := S256(new(big.Int).SetBytes(y[:])) - mod := new(big.Int).Mod(xb, yb) - res := LeftPadWord256(U256(mod).Bytes()) - stack.Push(res) - vm.Debugf(" %v %% %v = %v (%X)\n", xb, yb, mod, res) + mod := x.Mod(x, y) + res := stack.PushBigInt(mod) + vm.Debugf(" %v %% %v = %v (%X)\n", x, y, mod, res) } case ADDMOD: // 0x08 - x, y, z := stack.Pop(), stack.Pop(), stack.Pop() - if z.IsZero() { + x, y, z := stack.PopBigInt(), stack.PopBigInt(), stack.PopBigInt() + if z.Sign() == 0 { stack.Push(Zero256) vm.Debugf(" %v %% %v = %v\n", x, y, 0) } else { - xb := new(big.Int).SetBytes(x[:]) - yb := new(big.Int).SetBytes(y[:]) - zb := new(big.Int).SetBytes(z[:]) - add := new(big.Int).Add(xb, yb) - mod := new(big.Int).Mod(add, zb) - res := LeftPadWord256(U256(mod).Bytes()) - stack.Push(res) - vm.Debugf(" %v + %v %% %v = %v (%X)\n", - xb, yb, zb, mod, res) + add := x.Add(x, y) + mod := add.Mod(add, z) + res := stack.PushBigInt(mod) + vm.Debugf(" %v + %v %% %v = %v (%X)\n", x, y, z, mod, res) } case MULMOD: // 0x09 - x, y, z := stack.Pop(), stack.Pop(), stack.Pop() - if z.IsZero() { + x, y, z := stack.PopBigInt(), stack.PopBigInt(), stack.PopBigInt() + if z.Sign() == 0 { stack.Push(Zero256) vm.Debugf(" %v %% %v = %v\n", x, y, 0) } else { - xb := new(big.Int).SetBytes(x[:]) - yb := new(big.Int).SetBytes(y[:]) - zb := new(big.Int).SetBytes(z[:]) - mul := new(big.Int).Mul(xb, yb) - mod := new(big.Int).Mod(mul, zb) - res := LeftPadWord256(U256(mod).Bytes()) - stack.Push(res) - vm.Debugf(" %v * %v %% %v = %v (%X)\n", - xb, yb, zb, mod, res) + mul := x.Mul(x, y) + mod := mul.Mod(mul, z) + res := stack.PushBigInt(mod) + vm.Debugf(" %v * %v %% %v = %v (%X)\n", x, y, z, mod, res) } case EXP: // 0x0A - x, y := stack.Pop(), stack.Pop() - xb := new(big.Int).SetBytes(x[:]) - yb := new(big.Int).SetBytes(y[:]) - pow := new(big.Int).Exp(xb, yb, big.NewInt(0)) - res := LeftPadWord256(U256(pow).Bytes()) - stack.Push(res) - vm.Debugf(" %v ** %v = %v (%X)\n", xb, yb, pow, res) + x, y := stack.PopBigInt(), stack.PopBigInt() + pow := x.Exp(x, y, nil) + res := stack.PushBigInt(pow) + vm.Debugf(" %v ** %v = %v (%X)\n", x, y, pow, res) case SIGNEXTEND: // 0x0B - back := stack.Pop() - backb := new(big.Int).SetBytes(back[:]) - if backb.Cmp(big.NewInt(31)) < 0 { - bit := uint(backb.Uint64()*8 + 7) - num := stack.Pop() - numb := new(big.Int).SetBytes(num[:]) - mask := new(big.Int).Lsh(big.NewInt(1), bit) - mask.Sub(mask, big.NewInt(1)) - if numb.Bit(int(bit)) == 1 { - numb.Or(numb, mask.Not(mask)) - } else { - numb.Add(numb, mask) - } - res := LeftPadWord256(U256(numb).Bytes()) - vm.Debugf(" = %v (%X)", numb, res) - stack.Push(res) + back := stack.PopU64() + if back < Word256Length-1 { + stack.PushBigInt(SignExtend(back, stack.PopBigInt())) } case LT: // 0x10 - x, y := stack.Pop(), stack.Pop() - xb := new(big.Int).SetBytes(x[:]) - yb := new(big.Int).SetBytes(y[:]) - if xb.Cmp(yb) < 0 { - stack.Push64(1) - vm.Debugf(" %v < %v = %v\n", xb, yb, 1) + x, y := stack.PopBigInt(), stack.PopBigInt() + if x.Cmp(y) < 0 { + stack.Push(One256) + vm.Debugf(" %v < %v = %v\n", x, y, 1) } else { stack.Push(Zero256) - vm.Debugf(" %v < %v = %v\n", xb, yb, 0) + vm.Debugf(" %v < %v = %v\n", x, y, 0) } case GT: // 0x11 - x, y := stack.Pop(), stack.Pop() - xb := new(big.Int).SetBytes(x[:]) - yb := new(big.Int).SetBytes(y[:]) - if xb.Cmp(yb) > 0 { - stack.Push64(1) - vm.Debugf(" %v > %v = %v\n", xb, yb, 1) + x, y := stack.PopBigInt(), stack.PopBigInt() + if x.Cmp(y) > 0 { + stack.Push(One256) + vm.Debugf(" %v > %v = %v\n", x, y, 1) } else { stack.Push(Zero256) - vm.Debugf(" %v > %v = %v\n", xb, yb, 0) + vm.Debugf(" %v > %v = %v\n", x, y, 0) } case SLT: // 0x12 - x, y := stack.Pop(), stack.Pop() - xb := S256(new(big.Int).SetBytes(x[:])) - yb := S256(new(big.Int).SetBytes(y[:])) - if xb.Cmp(yb) < 0 { - stack.Push64(1) - vm.Debugf(" %v < %v = %v\n", xb, yb, 1) + x, y := stack.PopBigIntSigned(), stack.PopBigIntSigned() + if x.Cmp(y) < 0 { + stack.Push(One256) + vm.Debugf(" %v < %v = %v\n", x, y, 1) } else { stack.Push(Zero256) - vm.Debugf(" %v < %v = %v\n", xb, yb, 0) + vm.Debugf(" %v < %v = %v\n", x, y, 0) } case SGT: // 0x13 - x, y := stack.Pop(), stack.Pop() - xb := S256(new(big.Int).SetBytes(x[:])) - yb := S256(new(big.Int).SetBytes(y[:])) - if xb.Cmp(yb) > 0 { - stack.Push64(1) - vm.Debugf(" %v > %v = %v\n", xb, yb, 1) + x, y := stack.PopBigIntSigned(), stack.PopBigIntSigned() + if x.Cmp(y) > 0 { + stack.Push(One256) + vm.Debugf(" %v > %v = %v\n", x, y, 1) } else { stack.Push(Zero256) - vm.Debugf(" %v > %v = %v\n", xb, yb, 0) + vm.Debugf(" %v > %v = %v\n", x, y, 0) } case EQ: // 0x14 x, y := stack.Pop(), stack.Pop() if bytes.Equal(x[:], y[:]) { - stack.Push64(1) + stack.Push(One256) vm.Debugf(" %X == %X = %v\n", x, y, 1) } else { stack.Push(Zero256) @@ -425,7 +369,7 @@ func (vm *VM) call(caller, callee acm.MutableAccount, code, input []byte, value case ISZERO: // 0x15 x := stack.Pop() if x.IsZero() { - stack.Push64(1) + stack.Push(One256) vm.Debugf(" %X == 0 = %v\n", x, 1) } else { stack.Push(Zero256) @@ -481,7 +425,7 @@ func (vm *VM) call(caller, callee acm.MutableAccount, code, input []byte, value if useGasNegative(gas, GasSha3, &err) { return nil, err } - offset, size := stack.Pop64(), stack.Pop64() + offset, size := stack.PopBigInt(), stack.PopBigInt() data, memErr := memory.Read(offset, size) if memErr != nil { vm.Debugf(" => Memory err: %s", memErr) @@ -538,7 +482,7 @@ func (vm *VM) call(caller, callee acm.MutableAccount, code, input []byte, value vm.Debugf(" => %d\n", len(input)) case CALLDATACOPY: // 0x37 - memOff := stack.Pop64() + memOff := stack.PopBigInt() inputOff := stack.Pop64() length := stack.Pop64() data, ok := subslice(input, inputOff, length) @@ -558,7 +502,7 @@ func (vm *VM) call(caller, callee acm.MutableAccount, code, input []byte, value vm.Debugf(" => %d\n", l) case CODECOPY: // 0x39 - memOff := stack.Pop64() + memOff := stack.PopBigInt() codeOff := stack.Pop64() length := stack.Pop64() data, ok := subslice(code, codeOff, length) @@ -614,7 +558,7 @@ func (vm *VM) call(caller, callee acm.MutableAccount, code, input []byte, value return nil, firstErr(err, ErrUnknownAddress) } code := acc.Code() - memOff := stack.Pop64() + memOff := stack.PopBigInt() codeOff := stack.Pop64() length := stack.Pop64() data, ok := subslice(code, codeOff, length) @@ -655,8 +599,8 @@ func (vm *VM) call(caller, callee acm.MutableAccount, code, input []byte, value vm.Debugf(" => 0x%X\n", popped) case MLOAD: // 0x51 - offset := stack.Pop64() - data, memErr := memory.Read(offset, 32) + offset := stack.PopBigInt() + data, memErr := memory.Read(offset, BigWord256Length) if memErr != nil { vm.Debugf(" => Memory err: %s", memErr) return nil, firstErr(err, ErrMemoryOutOfBounds) @@ -665,7 +609,7 @@ func (vm *VM) call(caller, callee acm.MutableAccount, code, input []byte, value vm.Debugf(" => 0x%X @ 0x%X\n", data, offset) case MSTORE: // 0x52 - offset, data := stack.Pop64(), stack.Pop() + offset, data := stack.PopBigInt(), stack.Pop() memErr := memory.Write(offset, data.Bytes()) if memErr != nil { vm.Debugf(" => Memory err: %s", memErr) @@ -674,7 +618,7 @@ func (vm *VM) call(caller, callee acm.MutableAccount, code, input []byte, value vm.Debugf(" => 0x%X @ 0x%X\n", data, offset) case MSTORE8: // 0x53 - offset, val := stack.Pop64(), byte(stack.Pop64()&0xFF) + offset, val := stack.PopBigInt(), byte(stack.Pop64()&0xFF) memErr := memory.Write(offset, []byte{val}) if memErr != nil { vm.Debugf(" => Memory err: %s", memErr) @@ -726,7 +670,7 @@ func (vm *VM) call(caller, callee acm.MutableAccount, code, input []byte, value // free memory to be allocated for it if a subsequent MSTORE is made to // this offset. capacity := memory.Capacity() - stack.Push64(capacity) + stack.PushBigInt(capacity) vm.Debugf(" => 0x%X\n", capacity) case GAS: // 0x5A @@ -763,7 +707,7 @@ func (vm *VM) call(caller, callee acm.MutableAccount, code, input []byte, value case LOG0, LOG1, LOG2, LOG3, LOG4: n := int(op - LOG0) topics := make([]Word256, n) - offset, size := stack.Pop64(), stack.Pop64() + offset, size := stack.PopBigInt(), stack.PopBigInt() for i := 0; i < n; i++ { topics[i] = stack.Pop() } @@ -789,7 +733,7 @@ func (vm *VM) call(caller, callee acm.MutableAccount, code, input []byte, value return nil, ErrPermission{"create_contract"} } contractValue := stack.PopU64() - offset, size := stack.Pop64(), stack.Pop64() + offset, size := stack.PopBigInt(), stack.PopBigInt() input, memErr := memory.Read(offset, size) if memErr != nil { vm.Debugf(" => Memory err: %s", memErr) @@ -829,8 +773,8 @@ func (vm *VM) call(caller, callee acm.MutableAccount, code, input []byte, value if op != DELEGATECALL { value = stack.PopU64() } - inOffset, inSize := stack.Pop64(), stack.Pop64() // inputs - retOffset, retSize := stack.Pop64(), stack.Pop64() // outputs + inOffset, inSize := stack.PopBigInt(), stack.PopBigInt() // inputs + retOffset, retSize := stack.PopBigInt(), stack.Pop64() // outputs vm.Debugf(" => %X\n", addr) // Get the arguments from the memory @@ -924,7 +868,7 @@ func (vm *VM) call(caller, callee acm.MutableAccount, code, input []byte, value vm.Debugf("resume %s (%v)\n", callee.Address(), gas) case RETURN: // 0xF3 - offset, size := stack.Pop64(), stack.Pop64() + offset, size := stack.PopBigInt(), stack.PopBigInt() output, memErr := memory.Read(offset, size) if memErr != nil { vm.Debugf(" => Memory err: %s", memErr) diff --git a/execution/execution.go b/execution/execution.go index 09fd8c9f15dbc18be3d668c1c97cfa90df4a1092..caff450fc61c4264a01a164b4dd2f21a25d2bdcf 100644 --- a/execution/execution.go +++ b/execution/execution.go @@ -150,8 +150,14 @@ func (exe *executor) Reset() error { // If the tx is invalid, an error will be returned. // Unlike ExecBlock(), state will not be altered. -func (exe *executor) Execute(tx txs.Tx) error { +func (exe *executor) Execute(tx txs.Tx) (err error) { + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("recovered from panic in executor.Execute(%s): %v", tx.String(), r) + } + }() logger := logging.WithScope(exe.logger, "executor.Execute(tx txs.Tx)") + logging.TraceMsg(logger, "Executing transaction", "tx", tx.String()) // TODO: do something with fees fees := uint64(0) @@ -301,7 +307,6 @@ func (exe *executor) Execute(tx txs.Tx) error { // The logic in runCall MUST NOT return. if exe.runCall { - // VM call variables var ( gas uint64 = tx.GasLimit @@ -329,11 +334,11 @@ func (exe *executor) Execute(tx txs.Tx) error { // that will take your fees if outAcc == nil { logging.InfoMsg(logger, "Call to address that does not exist", - "caller_address", inAcc.Address, + "caller_address", inAcc.Address(), "callee_address", tx.Address) } else { logging.InfoMsg(logger, "Call to address that holds no code", - "caller_address", inAcc.Address, + "caller_address", inAcc.Address(), "callee_address", tx.Address) } err = txs.ErrTxInvalidAddress @@ -344,16 +349,17 @@ func (exe *executor) Execute(tx txs.Tx) error { if createContract { // We already checked for permission callee = evm.DeriveNewAccount(caller, permission.GlobalAccountPermissions(exe.state)) - logging.TraceMsg(logger, "Created new contract", - "contract_address", callee.Address, - "contract_code", callee.Code) code = tx.Data + logging.TraceMsg(logger, "Creating new contract", + "contract_address", callee.Address(), + "init_code", code) } else { callee = acm.AsMutableAccount(outAcc) - logging.TraceMsg(logger, "Calling existing contract", - "contract_address", callee.Address, - "contract_code", callee.Code) code = callee.Code() + logging.TraceMsg(logger, "Calling existing contract", + "contract_address", callee.Address(), + "input", tx.Data, + "contract_code", code) } logger.Trace("callee", callee.Address().String()) diff --git a/logging/config/filter_test.go b/logging/config/filter_test.go index 5e847acbbbec6f5510a5daf224acf361585519ce..9602fa71e5a41bdaeef8c7c703f0b7ca4265a099 100644 --- a/logging/config/filter_test.go +++ b/logging/config/filter_test.go @@ -115,5 +115,21 @@ func TestIncludeAnyFilterPredicate(t *testing.T) { assert.False(t, fp([]interface{}{"Foo", "bar", "Shoes", 3427})) assert.False(t, fp([]interface{}{"Foo", "bar", "Shoes", 42, "Bosh", "Bish"})) assert.False(t, fp([]interface{}{"Food", 0.2, "Shoes", 42})) +} + +func TestKeyOnlyPredicate(t *testing.T) { + fc := &FilterConfig{ + FilterMode: IncludeWhenAnyMatches, + Predicates: []*KeyValuePredicateConfig{ + { + KeyRegex: "Bosh", + }, + }, + } + fp, err := BuildFilterPredicate(fc) + assert.NoError(t, err) + assert.True(t, fp([]interface{}{"Foo", "bar", "Shoes", 3427})) + assert.False(t, fp([]interface{}{"Foo", "bar", "Shoes", 42, "Bosh", "Bish"})) + assert.True(t, fp([]interface{}{"Food", 0.2, "Shoes", 42})) } diff --git a/logging/lifecycle/lifecycle.go b/logging/lifecycle/lifecycle.go index 8d9428fd1b39e5141a2fb0d1429c864f28d29c27..e68a26021c129b918d8ea1b3d142d6fe441e6341 100644 --- a/logging/lifecycle/lifecycle.go +++ b/logging/lifecycle/lifecycle.go @@ -57,9 +57,7 @@ func NewLoggerFromLoggingConfig(loggingConfig *config.LoggingConfig) (types.Info go func() { err := <-errCh.Out() if err != nil { - logger.Info("logging_error", err, - "logging_config", loggingConfig.RootTOMLString(), - "logger", fmt.Sprintf("%#v", logger)) + fmt.Printf("Logging error: %v", err) } }() diff --git a/txs/tx.go b/txs/tx.go index 2d35e77620d8fbc043fd09dba182e37afe85e74f..c24018d44e02d716854810bd155b13af73a81f71 100644 --- a/txs/tx.go +++ b/txs/tx.go @@ -101,6 +101,7 @@ type ( // TODO: replace with sum-type struct like ResultEvent Tx interface { WriteSignBytes(chainID string, w io.Writer, n *int, err *error) + String() string } Wrapper struct {