diff --git a/binary/integer.go b/binary/integer.go index 396d6f151..443ac59a7 100644 --- a/binary/integer.go +++ b/binary/integer.go @@ -67,6 +67,24 @@ func SignExtend(x *big.Int, n uint) *big.Int { } } +// Reverse bytes in-place +func reverse(bs []byte) []byte { + n := len(bs) - 1 + for i := 0; i < len(bs)/2; i++ { + bs[i], bs[n-i] = bs[n-i], bs[i] + } + return bs +} + +// Note: this function destructively reverses its input slice - pass a copy if the original is used elsewhere +func BigIntFromLittleEndianBytes(bs []byte) *big.Int { + return new(big.Int).SetBytes(reverse(bs)) +} + +func BigIntToLittleEndianBytes(x *big.Int) []byte { + return reverse(x.Bytes()) +} + func andMask(n uint) *big.Int { x := new(big.Int) return x.Sub(x.Lsh(big1, n), big1) diff --git a/binary/integer_test.go b/binary/integer_test.go index 07abb7fff..2655ff381 100644 --- a/binary/integer_test.go +++ b/binary/integer_test.go @@ -133,6 +133,13 @@ func TestSignExtend(t *testing.T) { "0000 0000 0000 0000 0000 0000 0000 0000 0001 0000 1000 0000 1101 0011 1001 0000") } +func TestLittleEndian(t *testing.T) { + x, ok := new(big.Int).SetString("234890234579042368982348972347234789897", 10) + require.True(t, ok) + y := BigIntFromLittleEndianBytes(BigIntToLittleEndianBytes(x)) + require.Equal(t, x.Cmp(y), 0) +} + func assertSignExtend(t *testing.T, extendedBits int, embeddedBits uint, inputString, expectedString string) bool { input := intFromString(t, extendedBits, inputString) expected := intFromString(t, extendedBits, expectedString) diff --git a/execution/config.go b/execution/config.go index 09717d7de..dfecfca25 100644 --- a/execution/config.go +++ b/execution/config.go @@ -3,6 +3,8 @@ package execution import ( "fmt" + "github.com/hyperledger/burrow/execution/engine" + "github.com/hyperledger/burrow/execution/evm" ) @@ -45,7 +47,7 @@ func VMOptions(vmOptions evm.Options) func(*executor) { func (ec *ExecutionConfig) ExecutionOptions() ([]Option, error) { var exeOptions []Option vmOptions := evm.Options{ - MemoryProvider: evm.DefaultDynamicMemoryProvider, + MemoryProvider: engine.DefaultDynamicMemoryProvider, CallStackMaxDepth: ec.CallStackMaxDepth, DataStackInitialCapacity: ec.DataStackInitialCapacity, DataStackMaxDepth: ec.DataStackMaxDepth, diff --git a/execution/contexts/call_context.go b/execution/contexts/call_context.go index 178c92753..048d7d522 100644 --- a/execution/contexts/call_context.go +++ b/execution/contexts/call_context.go @@ -2,6 +2,7 @@ package contexts import ( "fmt" + "math/big" "github.com/hyperledger/burrow/acm" "github.com/hyperledger/burrow/acm/acmstate" @@ -10,7 +11,6 @@ import ( "github.com/hyperledger/burrow/execution/errors" "github.com/hyperledger/burrow/execution/evm" "github.com/hyperledger/burrow/execution/exec" - "github.com/hyperledger/burrow/execution/native" "github.com/hyperledger/burrow/execution/wasm" "github.com/hyperledger/burrow/logging" "github.com/hyperledger/burrow/logging/structure" @@ -135,7 +135,7 @@ func (ctx *CallContext) Deliver(inAcc, outAcc *acm.Account, value uint64) error callee = crypto.NewContractAddress(caller, ctx.txe.TxHash) code = ctx.tx.Data wcode = ctx.tx.WASM - err := native.CreateAccount(txCache, callee) + err := engine.CreateAccount(txCache, callee) if err != nil { return err } @@ -144,7 +144,7 @@ func (ctx *CallContext) Deliver(inAcc, outAcc *acm.Account, value uint64) error "init_code", code) // store abis - err = native.UpdateContractMeta(txCache, metaCache, callee, ctx.tx.ContractMeta) + err = engine.UpdateContractMeta(txCache, metaCache, callee, ctx.tx.ContractMeta) if err != nil { return err } @@ -184,19 +184,21 @@ func (ctx *CallContext) Deliver(inAcc, outAcc *acm.Account, value uint64) error var ret []byte var err error txHash := ctx.txe.Envelope.Tx.Hash() - gas := ctx.tx.GasLimit + gas := new(big.Int).SetUint64(ctx.tx.GasLimit) params := engine.CallParams{ Origin: caller, Caller: caller, Callee: callee, Input: ctx.tx.Data, - Value: value, - Gas: &gas, + Value: *new(big.Int).SetUint64(value), + Gas: gas, } if len(wcode) != 0 { - ret, err = wasm.RunWASM(txCache, params, wcode) + // TODO: accept options + vm := wasm.Default() + ret, err = vm.Execute(txCache, ctx.Blockchain, ctx.txe, params, wcode) if err != nil { // Failure. Charge the gas fee. The 'value' was otherwise not transferred. ctx.Logger.InfoMsg("Error on WASM execution", @@ -206,7 +208,7 @@ func (ctx *CallContext) Deliver(inAcc, outAcc *acm.Account, value uint64) error } else { ctx.Logger.TraceMsg("Successful execution") if createContract { - err := native.InitWASMCode(txCache, callee, ret) + err := engine.InitWASMCode(txCache, callee, ret) if err != nil { return err } @@ -233,7 +235,7 @@ func (ctx *CallContext) Deliver(inAcc, outAcc *acm.Account, value uint64) error } else { ctx.Logger.TraceMsg("Successful execution") if createContract { - err := native.InitEVMCode(txCache, callee, ret) + err := engine.InitEVMCode(txCache, callee, ret) if err != nil { return err } @@ -245,7 +247,8 @@ func (ctx *CallContext) Deliver(inAcc, outAcc *acm.Account, value uint64) error } ctx.CallEvents(err) } - ctx.txe.Return(ret, ctx.tx.GasLimit-gas) + // Gas starts life as a uint64 and should only been reduced (used up) over a transaction so .Uint64() is safe + ctx.txe.Return(ret, ctx.tx.GasLimit-gas.Uint64()) // Create a receipt from the ret and whether it erred. ctx.Logger.TraceMsg("VM Call complete", "caller", caller, diff --git a/execution/native/account.go b/execution/engine/account.go similarity index 83% rename from execution/native/account.go rename to execution/engine/account.go index ac57f4b6a..31ffd5905 100644 --- a/execution/native/account.go +++ b/execution/engine/account.go @@ -1,7 +1,8 @@ -package native +package engine import ( "bytes" + "math/big" "github.com/hyperledger/burrow/acm" "github.com/hyperledger/burrow/acm/acmstate" @@ -12,23 +13,6 @@ import ( "golang.org/x/crypto/sha3" ) -func CreateAccount(st acmstate.ReaderWriter, address crypto.Address) error { - acc, err := st.GetAccount(address) - if err != nil { - return err - } - if acc != nil { - if acc.NativeName != "" { - return errors.Errorf(errors.Codes.ReservedAddress, - "cannot create account at %v because that address is reserved for a native contract '%s'", - address, acc.NativeName) - } - return errors.Errorf(errors.Codes.DuplicateAddress, - "tried to create an account at an address that already exists: %v", address) - } - return st.UpdateAccount(&acm.Account{Address: address}) -} - func InitEVMCode(st acmstate.ReaderWriter, address crypto.Address, code []byte) error { return initEVMCode(st, address, nil, code) } @@ -38,7 +22,7 @@ func InitChildCode(st acmstate.ReaderWriter, address crypto.Address, parent cryp } func initEVMCode(st acmstate.ReaderWriter, address crypto.Address, parent *crypto.Address, code []byte) error { - acc, err := mustAccount(st, address) + acc, err := MustAccount(st, address) if err != nil { return err } @@ -116,7 +100,7 @@ func codehashPermitted(codehash []byte, metamap []*acm.ContractMeta) bool { } func InitWASMCode(st acmstate.ReaderWriter, address crypto.Address, code []byte) error { - acc, err := mustAccount(st, address) + acc, err := MustAccount(st, address) if err != nil { return err } @@ -133,25 +117,29 @@ func InitWASMCode(st acmstate.ReaderWriter, address crypto.Address, code []byte) return st.UpdateAccount(acc) } -func Transfer(st acmstate.ReaderWriter, from, to crypto.Address, amount uint64) error { - if amount == 0 { +// TODO: consider pushing big.Int usage all the way to account balance +func Transfer(st acmstate.ReaderWriter, from, to crypto.Address, amount *big.Int) error { + if !amount.IsInt64() { + return errors.Errorf(errors.Codes.IntegerOverflow, "transfer amount %v overflows int64", amount) + } + if amount.Sign() == 0 { return nil } - acc, err := mustAccount(st, from) + acc, err := MustAccount(st, from) if err != nil { return err } - if acc.Balance < amount { + if new(big.Int).SetUint64(acc.Balance).Cmp(amount) < 0 { return errors.Codes.InsufficientBalance } err = UpdateAccount(st, from, func(account *acm.Account) error { - return account.SubtractFromBalance(amount) + return account.SubtractFromBalance(amount.Uint64()) }) if err != nil { return err } return UpdateAccount(st, to, func(account *acm.Account) error { - return account.AddToBalance(amount) + return account.AddToBalance(amount.Uint64()) }) } @@ -159,7 +147,7 @@ func UpdateContractMeta(st acmstate.ReaderWriter, metaSt acmstate.MetadataWriter if len(payloadMeta) == 0 { return nil } - acc, err := mustAccount(st, address) + acc, err := MustAccount(st, address) if err != nil { return err } @@ -194,7 +182,7 @@ func RemoveAccount(st acmstate.ReaderWriter, address crypto.Address) error { } func UpdateAccount(st acmstate.ReaderWriter, address crypto.Address, updater func(acc *acm.Account) error) error { - acc, err := mustAccount(st, address) + acc, err := MustAccount(st, address) if err != nil { return err } @@ -205,7 +193,7 @@ func UpdateAccount(st acmstate.ReaderWriter, address crypto.Address, updater fun return st.UpdateAccount(acc) } -func mustAccount(st acmstate.Reader, address crypto.Address) (*acm.Account, error) { +func MustAccount(st acmstate.Reader, address crypto.Address) (*acm.Account, error) { acc, err := st.GetAccount(address) if err != nil { return nil, err diff --git a/execution/native/account_test.go b/execution/engine/account_test.go similarity index 91% rename from execution/native/account_test.go rename to execution/engine/account_test.go index 1510e4519..5e28cbb36 100644 --- a/execution/native/account_test.go +++ b/execution/engine/account_test.go @@ -1,4 +1,4 @@ -package native +package engine import ( "testing" @@ -6,7 +6,6 @@ import ( "github.com/hyperledger/burrow/acm" "github.com/hyperledger/burrow/acm/acmstate" "github.com/hyperledger/burrow/crypto" - "github.com/hyperledger/burrow/execution/engine" "github.com/hyperledger/burrow/execution/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -30,7 +29,7 @@ func TestState_CreateAccount(t *testing.T) { func TestState_Sync(t *testing.T) { backend := acmstate.NewCache(acmstate.NewMemoryState()) - st := engine.NewCallFrame(backend) + st := NewCallFrame(backend) address := AddressFromName("frogs") err := CreateAccount(st, address) @@ -46,7 +45,7 @@ func TestState_Sync(t *testing.T) { } func TestState_NewCache(t *testing.T) { - st := engine.NewCallFrame(acmstate.NewMemoryState()) + st := NewCallFrame(acmstate.NewMemoryState()) address := AddressFromName("frogs") cache, err := st.NewFrame() @@ -64,7 +63,7 @@ func TestState_NewCache(t *testing.T) { err = cache.Sync() require.NoError(t, err) - acc, err = mustAccount(cache, address) + acc, err = MustAccount(cache, address) require.NoError(t, err) assert.Equal(t, amt, acc.Balance) diff --git a/execution/engine/accounts.go b/execution/engine/accounts.go new file mode 100644 index 000000000..6faeee0d5 --- /dev/null +++ b/execution/engine/accounts.go @@ -0,0 +1,98 @@ +package engine + +import ( + "fmt" + + "github.com/hyperledger/burrow/acm" + "github.com/hyperledger/burrow/acm/acmstate" + "github.com/hyperledger/burrow/crypto" + "github.com/hyperledger/burrow/execution/errors" + "github.com/hyperledger/burrow/permission" +) + +type Maybe interface { + PushError(err error) bool + Error() error +} + +func GetAccount(st acmstate.Reader, m Maybe, address crypto.Address) *acm.Account { + acc, err := st.GetAccount(address) + if err != nil { + m.PushError(err) + return nil + } + return acc +} + +// Guaranteed to return a non-nil account, if the account does not exist returns a pointer to the zero-value of Account +// and pushes an error. +func MustGetAccount(st acmstate.Reader, m Maybe, address crypto.Address) *acm.Account { + acc := GetAccount(st, m, address) + if acc == nil { + m.PushError(errors.Errorf(errors.Codes.NonExistentAccount, "account %v does not exist", address)) + return &acm.Account{} + } + return acc +} + +func EnsurePermission(callFrame *CallFrame, address crypto.Address, perm permission.PermFlag) error { + hasPermission, err := HasPermission(callFrame, address, perm) + if err != nil { + return err + } else if !hasPermission { + return errors.PermissionDenied{ + Address: address, + Perm: perm, + } + } + return nil +} + +// CONTRACT: it is the duty of the contract writer to call known permissions +// we do not convey if a permission is not set +// (unlike in state/execution, where we guarantee HasPermission is called +// on known permissions and panics else) +// If the perm is not defined in the acc nor set by default in GlobalPermissions, +// this function returns false. +func HasPermission(st acmstate.Reader, address crypto.Address, perm permission.PermFlag) (bool, error) { + acc, err := st.GetAccount(address) + if err != nil { + return false, err + } + if acc == nil { + return false, fmt.Errorf("account %v does not exist", address) + } + globalPerms, err := acmstate.GlobalAccountPermissions(st) + if err != nil { + return false, err + } + perms := acc.Permissions.Base.Compose(globalPerms.Base) + value, err := perms.Get(perm) + if err != nil { + return false, err + } + return value, nil +} + +func CreateAccount(st acmstate.ReaderWriter, address crypto.Address) error { + acc, err := st.GetAccount(address) + if err != nil { + return err + } + if acc != nil { + if acc.NativeName != "" { + return errors.Errorf(errors.Codes.ReservedAddress, + "cannot create account at %v because that address is reserved for a native contract '%s'", + address, acc.NativeName) + } + return errors.Errorf(errors.Codes.DuplicateAddress, + "tried to create an account at an address that already exists: %v", address) + } + return st.UpdateAccount(&acm.Account{Address: address}) +} + +func AddressFromName(name string) (address crypto.Address) { + hash := crypto.Keccak256([]byte(name)) + copy(address[:], hash[len(hash)-crypto.AddressLength:]) + return +} diff --git a/execution/engine/blockchain.go b/execution/engine/blockchain.go new file mode 100644 index 000000000..292f9c58f --- /dev/null +++ b/execution/engine/blockchain.go @@ -0,0 +1,30 @@ +package engine + +import ( + "encoding/binary" + "time" + + "github.com/hyperledger/burrow/execution/errors" +) + +type TestBlockchain struct { + BlockHeight uint64 + BlockTime time.Time +} + +func (b *TestBlockchain) LastBlockHeight() uint64 { + return b.BlockHeight +} + +func (b *TestBlockchain) LastBlockTime() time.Time { + return b.BlockTime +} + +func (b *TestBlockchain) BlockHash(height uint64) ([]byte, error) { + if height > b.BlockHeight { + return nil, errors.Codes.InvalidBlockNumber + } + bs := make([]byte, 32) + binary.BigEndian.PutUint64(bs[24:], height) + return bs, nil +} diff --git a/execution/engine/call.go b/execution/engine/call.go new file mode 100644 index 000000000..3898ae6cf --- /dev/null +++ b/execution/engine/call.go @@ -0,0 +1,157 @@ +package engine + +import ( + "fmt" + "math/big" + + "github.com/hyperledger/burrow/execution/errors" + "github.com/hyperledger/burrow/execution/exec" + "github.com/hyperledger/burrow/permission" +) + +var big64 = big.NewInt(64) + +// Call provides a standard wrapper for implementing Callable.Call with appropriate error handling and event firing. +func Call(state State, params CallParams, execute func(State, CallParams) ([]byte, error)) ([]byte, error) { + maybe := new(errors.Maybe) + if params.CallType == exec.CallTypeCall || params.CallType == exec.CallTypeCode { + // NOTE: Delegate and Static CallTypes do not transfer the value to the callee. + maybe.PushError(Transfer(state.CallFrame, params.Caller, params.Callee, ¶ms.Value)) + } + + output := maybe.Bytes(execute(state, params)) + // fire the post call event (including exception if applicable) and make sure we return the accumulated call error + maybe.PushError(FireCallEvent(state.CallFrame, maybe.Error(), state.EventSink, output, params)) + return output, maybe.Error() +} + +func FireCallEvent(callFrame *CallFrame, callErr error, eventSink exec.EventSink, output []byte, + params CallParams) error { + // fire the post call event (including exception if applicable) + return eventSink.Call(&exec.CallEvent{ + CallType: params.CallType, + CallData: &exec.CallData{ + Caller: params.Caller, + Callee: params.Callee, + Data: params.Input, + Value: params.Value.Bytes(), + Gas: params.Gas.Bytes(), + }, + Origin: params.Origin, + StackDepth: callFrame.CallStackDepth(), + Return: output, + }, errors.AsException(callErr)) +} + +func CallFromSite(st State, dispatcher Dispatcher, site CallParams, target CallParams) ([]byte, error) { + err := EnsurePermission(st.CallFrame, site.Callee, permission.Call) + if err != nil { + return nil, err + } + // Get the arguments from the memory + // EVM contract + err = UseGasNegative(site.Gas, GasGetAccount) + if err != nil { + return nil, err + } + // since CALL is used also for sending funds, + // acc may not exist yet. This is an errors.CodedError for + // CALLCODE, but not for CALL, though I don't think + // ethereum actually cares + acc, err := st.CallFrame.GetAccount(target.Callee) + if err != nil { + return nil, err + } + if acc == nil { + if target.CallType != exec.CallTypeCall { + return nil, errors.Codes.UnknownAddress + } + // We're sending funds to a new account so we must create it first + err := st.CallFrame.CreateAccount(site.Callee, target.Callee) + if err != nil { + return nil, err + } + acc, err = st.CallFrame.GetAccount(target.Callee) + if err != nil { + return nil, err + } + } + + // Establish a stack frame and perform the call + childCallFrame, err := st.CallFrame.NewFrame() + if err != nil { + return nil, err + } + childState := State{ + CallFrame: childCallFrame, + Blockchain: st.Blockchain, + EventSink: st.EventSink, + } + // Ensure that gasLimit is reasonable + if site.Gas.Cmp(target.Gas) < 0 { + // EIP150 - the 63/64 rule - rather than errors.CodedError we pass this specified fraction of the total available gas + gas := new(big.Int) + target.Gas.Sub(site.Gas, gas.Div(site.Gas, big64)) + } + // NOTE: we will return any used gas later. + site.Gas.Sub(site.Gas, target.Gas) + + // Setup callee params for call type + target.Origin = site.Origin + + // Set up the caller/callee context + switch target.CallType { + case exec.CallTypeCall: + // Calls contract at target from this contract normally + // Value: transferred + // Caller: this contract + // Storage: target + // Code: from target + target.Caller = site.Callee + + case exec.CallTypeStatic: + // Calls contract at target from this contract with no state mutation + // Value: not transferred + // Caller: this contract + // Storage: target (read-only) + // Code: from target + target.Caller = site.Callee + + childState.CallFrame.ReadOnly() + childState.EventSink = exec.NewLogFreeEventSink(childState.EventSink) + + case exec.CallTypeCode: + // Calling this contract from itself as if it had the code at target + // Value: transferred + // Caller: this contract + // Storage: this contract + // Code: from target + + target.Caller = site.Callee + target.Callee = site.Callee + + case exec.CallTypeDelegate: + // Calling this contract from the original caller as if it had the code at target + // Value: not transferred + // Caller: original caller + // Storage: this contract + // Code: from target + + target.Caller = site.Caller + target.Callee = site.Callee + + default: + panic(fmt.Errorf("switch statement should be exhaustive so this should not have been reached")) + } + + returnData, err := dispatcher.Dispatch(acc).Call(childState, target) + + if err == nil { + // Sync error is a hard stop + err = childState.CallFrame.Sync() + } + + // Handle remaining gas. + //site.Gas.Add(site.Gas, target.Gas) + return returnData, err +} diff --git a/execution/engine/call_frame.go b/execution/engine/call_frame.go index b565dd511..602fb1a30 100644 --- a/execution/engine/call_frame.go +++ b/execution/engine/call_frame.go @@ -2,7 +2,9 @@ package engine import ( "github.com/hyperledger/burrow/acm/acmstate" + "github.com/hyperledger/burrow/crypto" "github.com/hyperledger/burrow/execution/errors" + "github.com/hyperledger/burrow/permission" ) type CallFrame struct { @@ -63,3 +65,11 @@ func (st *CallFrame) Sync() error { func (st *CallFrame) CallStackDepth() uint64 { return st.callStackDepth } + +func (st *CallFrame) CreateAccount(creator, address crypto.Address) error { + err := EnsurePermission(st, creator, permission.CreateAccount) + if err != nil { + return err + } + return CreateAccount(st, address) +} diff --git a/execution/engine/callable.go b/execution/engine/callable.go index 219d19257..6d2a492f9 100644 --- a/execution/engine/callable.go +++ b/execution/engine/callable.go @@ -1,6 +1,7 @@ package engine import ( + "math/big" "time" "github.com/hyperledger/burrow/crypto" @@ -19,14 +20,13 @@ type CallParams struct { Caller crypto.Address Callee crypto.Address Input []byte - Value uint64 - Gas *uint64 + Value big.Int + Gas *big.Int } // Effectively a contract, but can either represent a single function or a contract with multiple functions and a selector type Callable interface { Call(state State, params CallParams) (output []byte, err error) - // Fully qualified name of the callable, including any contract qualification } type CallableFunc func(st State, params CallParams) (output []byte, err error) diff --git a/execution/engine/dispatcher.go b/execution/engine/dispatcher.go index f0b74cf8c..793a31bd5 100644 --- a/execution/engine/dispatcher.go +++ b/execution/engine/dispatcher.go @@ -10,12 +10,35 @@ type Dispatcher interface { Dispatch(acc *acm.Account) Callable } +type DispatcherFunc func(acc *acm.Account) Callable + +func (d DispatcherFunc) Dispatch(acc *acm.Account) Callable { + return d(acc) +} + // An ExternalDispatcher is able to Dispatch accounts to external engines as well as Dispatch to itself type ExternalDispatcher interface { Dispatcher SetExternals(externals Dispatcher) } +// An ExternalDispatcher is able to Dispatch accounts to external engines as well as Dispatch to itself +type Externals struct { + // Provide any foreign dispatchers to allow calls between VMs + externals Dispatcher +} + +var _ ExternalDispatcher = (*Externals)(nil) + +func (ed *Externals) Dispatch(acc *acm.Account) Callable { + // Try external calls then fallback to EVM + return ed.externals.Dispatch(acc) +} + +func (ed *Externals) SetExternals(externals Dispatcher) { + ed.externals = externals +} + type Dispatchers []Dispatcher func NewDispatchers(dispatchers ...Dispatcher) Dispatchers { @@ -59,3 +82,6 @@ func (ds Dispatchers) Dispatch(acc *acm.Account) Callable { } return nil } + +type ExternalsStorage struct { +} diff --git a/execution/native/gas.go b/execution/engine/gas.go similarity index 55% rename from execution/native/gas.go rename to execution/engine/gas.go index cf50672dc..332c458e1 100644 --- a/execution/native/gas.go +++ b/execution/engine/gas.go @@ -1,7 +1,13 @@ // Copyright Monax Industries Limited // SPDX-License-Identifier: Apache-2.0 -package native +package engine + +import ( + "math/big" + + "github.com/hyperledger/burrow/execution/errors" +) const ( GasSha3 uint64 = 1 @@ -22,3 +28,15 @@ const ( GasIdentityWord uint64 = 1 GasIdentityBase uint64 = 1 ) + +// Try to deduct gasToUse from gasLeft. If ok return false, otherwise +// set err and return true. +func UseGasNegative(gasLeft *big.Int, gasToUse uint64) errors.CodedError { + delta := new(big.Int).SetUint64(gasToUse) + if gasLeft.Cmp(delta) >= 0 { + gasLeft.Sub(gasLeft, delta) + } else { + return errors.Codes.InsufficientGas + } + return nil +} diff --git a/execution/evm/memory.go b/execution/engine/memory.go similarity index 98% rename from execution/evm/memory.go rename to execution/engine/memory.go index 07a185ca5..9df1add22 100644 --- a/execution/evm/memory.go +++ b/execution/engine/memory.go @@ -1,4 +1,4 @@ -package evm +package engine import ( "fmt" @@ -159,3 +159,8 @@ func (mem *dynamicMemory) ensureCapacity(newCapacity uint64) error { mem.slice = mem.slice[:newCapacity] return nil } + +type fixedMemory struct { + slice []byte + errSink errors.Sink +} diff --git a/execution/evm/memory_test.go b/execution/engine/memory_test.go similarity index 99% rename from execution/evm/memory_test.go rename to execution/engine/memory_test.go index b88bf7388..32c8499a1 100644 --- a/execution/evm/memory_test.go +++ b/execution/engine/memory_test.go @@ -1,4 +1,4 @@ -package evm +package engine import ( "testing" diff --git a/execution/evm/contract.go b/execution/evm/contract.go index b5d1b3f98..65c5f354a 100644 --- a/execution/evm/contract.go +++ b/execution/evm/contract.go @@ -9,16 +9,13 @@ import ( "strings" "github.com/hyperledger/burrow/acm" - "github.com/hyperledger/burrow/acm/acmstate" . "github.com/hyperledger/burrow/binary" "github.com/hyperledger/burrow/crypto" - "github.com/hyperledger/burrow/execution/engine" "github.com/hyperledger/burrow/execution/errors" "github.com/hyperledger/burrow/execution/evm/abi" . "github.com/hyperledger/burrow/execution/evm/asm" "github.com/hyperledger/burrow/execution/exec" - "github.com/hyperledger/burrow/execution/native" "github.com/hyperledger/burrow/permission" "github.com/hyperledger/burrow/txs" ) @@ -29,7 +26,7 @@ type Contract struct { } func (c *Contract) Call(state engine.State, params engine.CallParams) ([]byte, error) { - return native.Call(state, params, c.execute) + return engine.Call(state, params, c.execute) } // Executes the EVM code passed in the appropriate context @@ -65,9 +62,9 @@ func (c *Contract) execute(st engine.State, params engine.CallParams) ([]byte, e } var op = c.GetSymbol(pc) - c.debugf("(pc) %-3d (op) %-14s (st) %-4d (gas) %d", pc, op.String(), stack.Len(), *params.Gas) + c.debugf("(pc) %-3d (op) %-14s (st) %-4d (gas) %d", pc, op.String(), stack.Len(), params.Gas) // Use BaseOp gas. - maybe.PushError(useGasNegative(params.Gas, native.GasBaseOp)) + maybe.PushError(engine.UseGasNegative(params.Gas, engine.GasBaseOp)) switch op { @@ -321,7 +318,7 @@ func (c *Contract) execute(st engine.State, params engine.CallParams) ([]byte, e } case SHA3: // 0x20 - maybe.PushError(useGasNegative(params.Gas, native.GasSha3)) + maybe.PushError(engine.UseGasNegative(params.Gas, engine.GasSha3)) offset, size := stack.PopBigInt(), stack.PopBigInt() data := memory.Read(offset, size) data = crypto.Keccak256(data) @@ -334,8 +331,8 @@ func (c *Contract) execute(st engine.State, params engine.CallParams) ([]byte, e case BALANCE: // 0x31 address := stack.PopAddress() - maybe.PushError(useGasNegative(params.Gas, native.GasGetAccount)) - balance := mustGetAccount(st.CallFrame, maybe, address).Balance + maybe.PushError(engine.UseGasNegative(params.Gas, engine.GasGetAccount)) + balance := engine.MustGetAccount(st.CallFrame, maybe, address).Balance stack.Push64(balance) c.debugf(" => %v (%v)\n", balance, address) @@ -348,7 +345,7 @@ func (c *Contract) execute(st engine.State, params engine.CallParams) ([]byte, e c.debugf(" => %v\n", params.Caller) case CALLVALUE: // 0x34 - stack.Push64(params.Value) + stack.PushBigInt(¶ms.Value) c.debugf(" => %v\n", params.Value) case CALLDATALOAD: // 0x35 @@ -389,8 +386,8 @@ func (c *Contract) execute(st engine.State, params engine.CallParams) ([]byte, e case EXTCODESIZE: // 0x3B address := stack.PopAddress() - maybe.PushError(useGasNegative(params.Gas, native.GasGetAccount)) - acc := mustGetAccount(st.CallFrame, maybe, address) + maybe.PushError(engine.UseGasNegative(params.Gas, engine.GasGetAccount)) + acc := engine.MustGetAccount(st.CallFrame, maybe, address) if acc == nil { stack.Push(Zero256) c.debugf(" => 0\n") @@ -401,8 +398,8 @@ func (c *Contract) execute(st engine.State, params engine.CallParams) ([]byte, e } case EXTCODECOPY: // 0x3C address := stack.PopAddress() - maybe.PushError(useGasNegative(params.Gas, native.GasGetAccount)) - acc := mustGetAccount(st.CallFrame, maybe, address) + maybe.PushError(engine.UseGasNegative(params.Gas, engine.GasGetAccount)) + acc := engine.MustGetAccount(st.CallFrame, maybe, address) if acc == nil { maybe.PushError(errors.Codes.UnknownAddress) } else { @@ -434,7 +431,7 @@ func (c *Contract) execute(st engine.State, params engine.CallParams) ([]byte, e case EXTCODEHASH: // 0x3F address := stack.PopAddress() - acc := getAccount(st.CallFrame, maybe, address) + acc := engine.GetAccount(st.CallFrame, maybe, address) if acc == nil { // In case the account does not exist 0 is pushed to the stack. stack.Push64(0) @@ -482,7 +479,7 @@ func (c *Contract) execute(st engine.State, params engine.CallParams) ([]byte, e c.debugf(" => %d\n", number) case GASLIMIT: // 0x45 - stack.Push64(*params.Gas) + stack.PushBigInt(params.Gas) c.debugf(" => %v\n", *params.Gas) case POP: // 0x50 @@ -515,7 +512,7 @@ func (c *Contract) execute(st engine.State, params engine.CallParams) ([]byte, e case SSTORE: // 0x55 loc, data := stack.Pop(), stack.Pop() - maybe.PushError(useGasNegative(params.Gas, native.GasStorageUpdate)) + maybe.PushError(engine.UseGasNegative(params.Gas, engine.GasStorageUpdate)) maybe.PushError(st.CallFrame.SetStorage(params.Callee, loc, data.Bytes())) c.debugf("%v {%v := %v}\n", params.Callee, loc, data) @@ -546,8 +543,8 @@ func (c *Contract) execute(st engine.State, params engine.CallParams) ([]byte, e c.debugf(" => 0x%X\n", capacity) case GAS: // 0x5A - stack.Push64(*params.Gas) - c.debugf(" => %X\n", *params.Gas) + stack.PushBigInt(params.Gas) + c.debugf(" => %X\n", params.Gas) case JUMPDEST: // 0x5B c.debugf("\n") @@ -588,12 +585,12 @@ func (c *Contract) execute(st engine.State, params engine.CallParams) ([]byte, e case CREATE, CREATE2: // 0xF0, 0xFB returnData = nil - contractValue := stack.Pop64() + contractValue := stack.PopBigInt() offset, size := stack.PopBigInt(), stack.PopBigInt() input := memory.Read(offset, size) // TODO charge for gas to create account _ the code length * GasCreateByte - maybe.PushError(useGasNegative(params.Gas, native.GasCreateAccount)) + maybe.PushError(engine.UseGasNegative(params.Gas, engine.GasCreateAccount)) var newAccountAddress crypto.Address if op == CREATE { @@ -604,19 +601,19 @@ func (c *Contract) execute(st engine.State, params engine.CallParams) ([]byte, e newAccountAddress = crypto.NewContractAddress(params.Callee, nonce) } else if op == CREATE2 { salt := stack.Pop() - code := mustGetAccount(st.CallFrame, maybe, params.Callee).EVMCode + code := engine.MustGetAccount(st.CallFrame, maybe, params.Callee).EVMCode newAccountAddress = crypto.NewContractAddress2(params.Callee, salt, code) } // Check the CreateContract permission for this account - if maybe.PushError(ensurePermission(st.CallFrame, params.Callee, permission.CreateContract)) { + if maybe.PushError(engine.EnsurePermission(st.CallFrame, params.Callee, permission.CreateContract)) { continue } // Establish a frame in which the putative account exists childCallFrame, err := st.CallFrame.NewFrame() maybe.PushError(err) - maybe.PushError(native.CreateAccount(childCallFrame, newAccountAddress)) + maybe.PushError(engine.CreateAccount(childCallFrame, newAccountAddress)) // Run the input to get the contract code. // NOTE: no need to copy 'input' as per Call contract. @@ -631,7 +628,7 @@ func (c *Contract) execute(st engine.State, params engine.CallParams) ([]byte, e Caller: params.Callee, Callee: newAccountAddress, Input: input, - Value: contractValue, + Value: *contractValue, Gas: params.Gas, }) if callErr != nil { @@ -641,7 +638,7 @@ func (c *Contract) execute(st engine.State, params engine.CallParams) ([]byte, e returnData = ret } else { // Update the account with its initialised contract code - maybe.PushError(native.InitChildCode(childCallFrame, newAccountAddress, params.Callee, ret)) + maybe.PushError(engine.InitChildCode(childCallFrame, newAccountAddress, params.Callee, ret)) maybe.PushError(childCallFrame.Sync()) stack.PushAddress(newAccountAddress) } @@ -649,11 +646,11 @@ func (c *Contract) execute(st engine.State, params engine.CallParams) ([]byte, e case CALL, CALLCODE, DELEGATECALL, STATICCALL: // 0xF1, 0xF2, 0xF4, 0xFA returnData = nil - if maybe.PushError(ensurePermission(st.CallFrame, params.Callee, permission.Call)) { + if maybe.PushError(engine.EnsurePermission(st.CallFrame, params.Callee, permission.Call)) { continue } // Pull arguments off stack: - gasLimit := stack.Pop64() + gasLimit := stack.PopBigInt() target := stack.PopAddress() value := params.Value // NOTE: for DELEGATECALL value is preserved from the original @@ -662,7 +659,7 @@ func (c *Contract) execute(st engine.State, params engine.CallParams) ([]byte, e // caller value is used. for CALL and CALLCODE value is stored // on stack and needs to be overwritten from the given value. if op != DELEGATECALL && op != STATICCALL { - value = stack.Pop64() + value = *stack.PopBigInt() } // inputs inOffset, inSize := stack.PopBigInt(), stack.PopBigInt() @@ -671,134 +668,34 @@ func (c *Contract) execute(st engine.State, params engine.CallParams) ([]byte, e retSize := stack.Pop64() c.debugf(" => %v\n", target) - // Get the arguments from the memory - // EVM contract - maybe.PushError(useGasNegative(params.Gas, native.GasGetAccount)) - // since CALL is used also for sending funds, - // acc may not exist yet. This is an errors.CodedError for - // CALLCODE, but not for CALL, though I don't think - // ethereum actually cares - acc := getAccount(st.CallFrame, maybe, target) - if acc == nil { - if op != CALL { - maybe.PushError(errors.Codes.UnknownAddress) - continue - } - // We're sending funds to a new account so we must create it first - if maybe.PushError(createAccount(st.CallFrame, params.Callee, target)) { - continue - } - acc = mustGetAccount(st.CallFrame, maybe, target) - } - - // Establish a stack frame and perform the call - childCallFrame, err := st.CallFrame.NewFrame() - if maybe.PushError(err) { - continue - } - childState := engine.State{ - CallFrame: childCallFrame, - Blockchain: st.Blockchain, - EventSink: st.EventSink, - } - // Ensure that gasLimit is reasonable - if *params.Gas < gasLimit { - // EIP150 - the 63/64 rule - rather than errors.CodedError we pass this specified fraction of the total available gas - gasLimit = *params.Gas - *params.Gas/64 - } - // NOTE: we will return any used gas later. - *params.Gas -= gasLimit - - // Setup callee params for call type - - calleeParams := engine.CallParams{ - Origin: params.Origin, - Input: memory.Read(inOffset, inSize), - Value: value, - Gas: &gasLimit, - } - - // Set up the caller/callee context - switch op { - case CALL: - // Calls contract at target from this contract normally - // Value: transferred - // Caller: this contract - // Storage: target - // Code: from target - - calleeParams.CallType = exec.CallTypeCall - calleeParams.Caller = params.Callee - calleeParams.Callee = target - - case STATICCALL: - // Calls contract at target from this contract with no state mutation - // Value: not transferred - // Caller: this contract - // Storage: target (read-only) - // Code: from target - - calleeParams.CallType = exec.CallTypeStatic - calleeParams.Caller = params.Callee - calleeParams.Callee = target - - childState.CallFrame.ReadOnly() - childState.EventSink = exec.NewLogFreeEventSink(childState.EventSink) - - case CALLCODE: - // Calling this contract from itself as if it had the code at target - // Value: transferred - // Caller: this contract - // Storage: this contract - // Code: from target - - calleeParams.CallType = exec.CallTypeCode - calleeParams.Caller = params.Callee - calleeParams.Callee = params.Callee - - case DELEGATECALL: - // Calling this contract from the original caller as if it had the code at target - // Value: not transferred - // Caller: original caller - // Storage: this contract - // Code: from target - - calleeParams.CallType = exec.CallTypeDelegate - calleeParams.Caller = params.Caller - calleeParams.Callee = params.Callee - - default: - panic(fmt.Errorf("switch statement should be exhaustive so this should not have been reached")) - } - - var callErr error - returnData, callErr = c.Dispatch(acc).Call(childState, calleeParams) - - if callErr == nil { - // Sync error is a hard stop - maybe.PushError(childState.CallFrame.Sync()) - } - + var err error + returnData, err = engine.CallFromSite(st, c, params, engine.CallParams{ + CallType: callTypeFromOpCode(op), + Callee: target, + Input: memory.Read(inOffset, inSize), + Value: value, + Gas: gasLimit, + }) // Push result - if callErr != nil { - c.debugf("error from nested sub-call (depth: %v): %s\n", st.CallFrame.CallStackDepth(), callErr.Error()) + if err != nil { + c.debugf("error from nested sub-call (depth: %v): %s\n", st.CallFrame.CallStackDepth(), err) // So we can return nested errors.CodedError if the top level return is an errors.CodedError stack.Push(Zero256) - if errors.GetCode(callErr) == errors.Codes.ExecutionReverted { - memory.Write(retOffset, RightPadBytes(returnData, int(retSize))) - } } else { stack.Push(One256) + } - // Should probably only be necessary when there is no return value and - // returnData is empty, but since EVM expects retSize to be respected this will - // defensively pad or truncate the portion of returnData to be returned. + code := errors.GetCode(err) + if code == errors.Codes.None || code == errors.Codes.ExecutionReverted { memory.Write(retOffset, RightPadBytes(returnData, int(retSize))) + } else { + maybe.PushError(err) } - // Handle remaining gas. - *params.Gas += *calleeParams.Gas + // TODO: decide how to handle this + // Apply refund of any unused gas + params.Gas.Add(params.Gas, gasLimit) c.debugf("resume %s (%v)\n", params.Callee, params.Gas) @@ -821,19 +718,19 @@ func (c *Contract) execute(st engine.State, params engine.CallParams) ([]byte, e case SELFDESTRUCT: // 0xFF receiver := stack.PopAddress() - maybe.PushError(useGasNegative(params.Gas, native.GasGetAccount)) - if getAccount(st.CallFrame, maybe, receiver) == nil { + maybe.PushError(engine.UseGasNegative(params.Gas, engine.GasGetAccount)) + if engine.GetAccount(st.CallFrame, maybe, receiver) == nil { // If receiver address doesn't exist, try to create it - maybe.PushError(useGasNegative(params.Gas, native.GasCreateAccount)) - if maybe.PushError(createAccount(st.CallFrame, params.Callee, receiver)) { + maybe.PushError(engine.UseGasNegative(params.Gas, engine.GasCreateAccount)) + if maybe.PushError(st.CallFrame.CreateAccount(params.Callee, receiver)) { continue } } - balance := mustGetAccount(st.CallFrame, maybe, params.Callee).Balance - maybe.PushError(native.UpdateAccount(st.CallFrame, receiver, func(account *acm.Account) error { + balance := engine.MustGetAccount(st.CallFrame, maybe, params.Callee).Balance + maybe.PushError(engine.UpdateAccount(st.CallFrame, receiver, func(account *acm.Account) error { return account.AddToBalance(balance) })) - maybe.PushError(native.RemoveAccount(st.CallFrame, params.Callee)) + maybe.PushError(engine.RemoveAccount(st.CallFrame, params.Callee)) c.debugf(" => (%X) %v\n", receiver[:4], balance) return nil, maybe.Error() @@ -862,58 +759,6 @@ func (c *Contract) jump(to uint64, pc *uint64) error { return nil } -func createAccount(callFrame *engine.CallFrame, creator, address crypto.Address) error { - err := ensurePermission(callFrame, creator, permission.CreateAccount) - if err != nil { - return err - } - return native.CreateAccount(callFrame, address) -} - -func getAccount(st acmstate.Reader, m *errors.Maybe, address crypto.Address) *acm.Account { - acc, err := st.GetAccount(address) - if err != nil { - m.PushError(err) - return nil - } - return acc -} - -// Guaranteed to return a non-nil account, if the account does not exist returns a pointer to the zero-value of Account -// and pushes an error. -func mustGetAccount(st acmstate.Reader, m *errors.Maybe, address crypto.Address) *acm.Account { - acc := getAccount(st, m, address) - if acc == nil { - m.PushError(errors.Errorf(errors.Codes.NonExistentAccount, "account %v does not exist", address)) - return &acm.Account{} - } - return acc -} - -func ensurePermission(callFrame *engine.CallFrame, address crypto.Address, perm permission.PermFlag) error { - hasPermission, err := native.HasPermission(callFrame, address, perm) - if err != nil { - return err - } else if !hasPermission { - return errors.PermissionDenied{ - Address: address, - Perm: perm, - } - } - return nil -} - -// Try to deduct gasToUse from gasLeft. If ok return false, otherwise -// set err and return true. -func useGasNegative(gasLeft *uint64, gasToUse uint64) error { - if *gasLeft >= gasToUse { - *gasLeft -= gasToUse - } else { - return errors.Codes.InsufficientGas - } - return nil -} - // Returns a subslice from offset of length length and a bool // (true iff slice was possible). If the subslice // extends past the end of data it returns A COPY of the segment at the end of @@ -934,14 +779,6 @@ func subslice(data []byte, offset, length uint64) ([]byte, error) { return data[offset : offset+length], nil } -func codeGetOp(code []byte, n uint64) OpCode { - if uint64(len(code)) <= n { - return OpCode(0) // stop - } else { - return OpCode(code[n]) - } -} - // Dump the bytecode being sent to the EVM in the current working directory func dumpTokens(nonce []byte, caller, callee crypto.Address, code []byte) { var tokensString string @@ -978,3 +815,18 @@ func newRevertException(ret []byte) errors.CodedError { } return code } + +func callTypeFromOpCode(o OpCode) exec.CallType { + switch o { + case CALL: + return exec.CallTypeCall + case CALLCODE: + return exec.CallTypeCode + case STATICCALL: + return exec.CallTypeStatic + case DELEGATECALL: + return exec.CallTypeDelegate + default: + return exec.CallTypeInvalid + } +} diff --git a/execution/evm/evm.go b/execution/evm/evm.go index c8b28d4b9..ee569b522 100644 --- a/execution/evm/evm.go +++ b/execution/evm/evm.go @@ -25,7 +25,7 @@ type EVM struct { options Options sequence uint64 // Provide any foreign dispatchers to allow calls between VMs - externals engine.Dispatcher + engine.Externals // User dispatcher.CallableProvider to get access to other VMs logger *logging.Logger } @@ -33,7 +33,7 @@ type EVM struct { // Options are parameters that are generally stable across a burrow configuration. // Defaults will be used for any zero values. type Options struct { - MemoryProvider func(errors.Sink) Memory + MemoryProvider func(errors.Sink) engine.Memory Natives *native.Natives Nonce []byte DebugOpcodes bool @@ -47,7 +47,7 @@ type Options struct { func New(options Options) *EVM { // Set defaults if options.MemoryProvider == nil { - options.MemoryProvider = DefaultDynamicMemoryProvider + options.MemoryProvider = engine.DefaultDynamicMemoryProvider } if options.Logger == nil { options.Logger = logging.NewNoopLogger() @@ -105,19 +105,13 @@ func (vm *EVM) SetLogger(logger *logging.Logger) { } func (vm *EVM) Dispatch(acc *acm.Account) engine.Callable { - // Try external calls then fallback to EVM - callable := vm.externals.Dispatch(acc) + callable := vm.Externals.Dispatch(acc) if callable != nil { return callable } - // This supports empty code calls return vm.Contract(acc.EVMCode) } -func (vm *EVM) SetExternals(externals engine.Dispatcher) { - vm.externals = externals -} - func (vm *EVM) Contract(code []byte) *Contract { return &Contract{ EVM: vm, diff --git a/execution/evm/evm_test.go b/execution/evm/evm_test.go index aba3643c7..342cc1855 100644 --- a/execution/evm/evm_test.go +++ b/execution/evm/evm_test.go @@ -40,15 +40,13 @@ func TestEVM(t *testing.T) { t.Run("BasicLoop", func(t *testing.T) { st := acmstate.NewMemoryState() - blockchain := new(blockchain) + blockchain := new(engine.TestBlockchain) eventSink := exec.NewNoopEventSink() // Create accounts account1 := newAccount(t, st, "1") account2 := newAccount(t, st, "101") - var gas uint64 = 100000 - bytecode := MustSplice(PUSH1, 0x00, PUSH1, 0x20, MSTORE, JUMPDEST, PUSH2, 0x0F, 0x0F, PUSH1, 0x20, MLOAD, SLT, ISZERO, PUSH1, 0x1D, JUMPI, PUSH1, 0x01, PUSH1, 0x20, MLOAD, ADD, PUSH1, 0x20, MSTORE, PUSH1, 0x05, JUMP, JUMPDEST) @@ -57,7 +55,7 @@ func TestEVM(t *testing.T) { output, err := vm.Execute(st, blockchain, eventSink, engine.CallParams{ Caller: account1, Callee: account2, - Gas: &gas, + Gas: big.NewInt(100000), }, bytecode) t.Logf("Output: %v Error: %v\n", output, err) t.Logf("Call took: %v", time.Since(start)) @@ -69,11 +67,11 @@ func TestEVM(t *testing.T) { account1 := newAccount(t, st, "1") account2 := newAccount(t, st, "101") - var gas uint64 = 100000 + gas := big.NewInt(100000) //Shift left 0 bytecode := MustSplice(PUSH1, 0x01, PUSH1, 0x00, SHL, return1()) - output, err := call(vm, st, account1, account2, bytecode, nil, &gas) + output, err := call(vm, st, account1, account2, bytecode, nil, gas) value := []byte{0x1} expected := LeftPadBytes(value, 32) assert.Equal(t, expected, output) @@ -87,7 +85,7 @@ func TestEVM(t *testing.T) { //Alternative shift left 0 bytecode = MustSplice(PUSH32, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, PUSH1, 0x00, SHL, return1()) - output, err = call(vm, st, account1, account2, bytecode, nil, &gas) + output, err = call(vm, st, account1, account2, bytecode, nil, gas) expected = []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} @@ -101,7 +99,7 @@ func TestEVM(t *testing.T) { //Shift left 1 bytecode = MustSplice(PUSH1, 0x01, PUSH1, 0x01, SHL, return1()) - output, err = call(vm, st, account1, account2, bytecode, nil, &gas) + output, err = call(vm, st, account1, account2, bytecode, nil, gas) value = []byte{0x2} expected = LeftPadBytes(value, 32) assert.Equal(t, expected, output) @@ -115,7 +113,7 @@ func TestEVM(t *testing.T) { //Alternative shift left 1 bytecode = MustSplice(PUSH32, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, PUSH1, 0x01, SHL, return1()) - output, err = call(vm, st, account1, account2, bytecode, nil, &gas) + output, err = call(vm, st, account1, account2, bytecode, nil, gas) value = []byte{0x2} expected = []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE} @@ -131,7 +129,7 @@ func TestEVM(t *testing.T) { //Alternative shift left 1 bytecode = MustSplice(PUSH32, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, PUSH1, 0x01, SHL, return1()) - output, err = call(vm, st, account1, account2, bytecode, nil, &gas) + output, err = call(vm, st, account1, account2, bytecode, nil, gas) expected = []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE} @@ -145,7 +143,7 @@ func TestEVM(t *testing.T) { //Shift left 255 bytecode = MustSplice(PUSH1, 0x01, PUSH1, 0xFF, SHL, return1()) - output, err = call(vm, st, account1, account2, bytecode, nil, &gas) + output, err = call(vm, st, account1, account2, bytecode, nil, gas) value = []byte{0x80} expected = RightPadBytes(value, 32) assert.Equal(t, expected, output) @@ -159,7 +157,7 @@ func TestEVM(t *testing.T) { //Alternative shift left 255 bytecode = MustSplice(PUSH32, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, PUSH1, 0xFF, SHL, return1()) - output, err = call(vm, st, account1, account2, bytecode, nil, &gas) + output, err = call(vm, st, account1, account2, bytecode, nil, gas) value = []byte{0x80} expected = RightPadBytes(value, 32) assert.Equal(t, expected, output) @@ -172,7 +170,7 @@ func TestEVM(t *testing.T) { //Shift left 256 (overflow) bytecode = MustSplice(PUSH1, 0x01, PUSH2, 0x01, 0x00, SHL, return1()) - output, err = call(vm, st, account1, account2, bytecode, nil, &gas) + output, err = call(vm, st, account1, account2, bytecode, nil, gas) value = []byte{0x00} expected = LeftPadBytes(value, 32) assert.Equal(t, expected, output) @@ -187,7 +185,7 @@ func TestEVM(t *testing.T) { bytecode = MustSplice(PUSH32, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, PUSH2, 0x01, 0x00, SHL, return1()) - output, err = call(vm, st, account1, account2, bytecode, nil, &gas) + output, err = call(vm, st, account1, account2, bytecode, nil, gas) value = []byte{0x00} expected = LeftPadBytes(value, 32) assert.Equal(t, expected, output) @@ -200,7 +198,7 @@ func TestEVM(t *testing.T) { //Shift left 257 (overflow) bytecode = MustSplice(PUSH1, 0x01, PUSH2, 0x01, 0x01, SHL, return1()) - output, err = call(vm, st, account1, account2, bytecode, nil, &gas) + output, err = call(vm, st, account1, account2, bytecode, nil, gas) value = []byte{0x00} expected = LeftPadBytes(value, 32) assert.Equal(t, expected, output) @@ -217,11 +215,11 @@ func TestEVM(t *testing.T) { account1 := newAccount(t, st, "1") account2 := newAccount(t, st, "101") - var gas uint64 = 100000 + gas := big.NewInt(100000) //Shift right 0 bytecode := MustSplice(PUSH1, 0x01, PUSH1, 0x00, SHR, return1()) - output, err := call(vm, st, account1, account2, bytecode, nil, &gas) + output, err := call(vm, st, account1, account2, bytecode, nil, gas) value := []byte{0x1} expected := LeftPadBytes(value, 32) assert.Equal(t, expected, output) @@ -235,7 +233,7 @@ func TestEVM(t *testing.T) { //Alternative shift right 0 bytecode = MustSplice(PUSH32, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, PUSH1, 0x00, SHR, return1()) - output, err = call(vm, st, account1, account2, bytecode, nil, &gas) + output, err = call(vm, st, account1, account2, bytecode, nil, gas) expected = []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} @@ -249,7 +247,7 @@ func TestEVM(t *testing.T) { //Shift right 1 bytecode = MustSplice(PUSH1, 0x01, PUSH1, 0x01, SHR, return1()) - output, err = call(vm, st, account1, account2, bytecode, nil, &gas) + output, err = call(vm, st, account1, account2, bytecode, nil, gas) value = []byte{0x00} expected = LeftPadBytes(value, 32) assert.Equal(t, expected, output) @@ -263,7 +261,7 @@ func TestEVM(t *testing.T) { //Alternative shift right 1 bytecode = MustSplice(PUSH32, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, PUSH1, 0x01, SHR, return1()) - output, err = call(vm, st, account1, account2, bytecode, nil, &gas) + output, err = call(vm, st, account1, account2, bytecode, nil, gas) value = []byte{0x40} expected = RightPadBytes(value, 32) assert.Equal(t, expected, output) @@ -277,7 +275,7 @@ func TestEVM(t *testing.T) { //Alternative shift right 1 bytecode = MustSplice(PUSH32, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, PUSH1, 0x01, SHR, return1()) - output, err = call(vm, st, account1, account2, bytecode, nil, &gas) + output, err = call(vm, st, account1, account2, bytecode, nil, gas) expected = []byte{0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} @@ -292,7 +290,7 @@ func TestEVM(t *testing.T) { //Shift right 255 bytecode = MustSplice(PUSH32, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, PUSH1, 0xFF, SHR, return1()) - output, err = call(vm, st, account1, account2, bytecode, nil, &gas) + output, err = call(vm, st, account1, account2, bytecode, nil, gas) value = []byte{0x1} expected = LeftPadBytes(value, 32) assert.Equal(t, expected, output) @@ -306,7 +304,7 @@ func TestEVM(t *testing.T) { //Alternative shift right 255 bytecode = MustSplice(PUSH32, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, PUSH1, 0xFF, SHR, return1()) - output, err = call(vm, st, account1, account2, bytecode, nil, &gas) + output, err = call(vm, st, account1, account2, bytecode, nil, gas) value = []byte{0x1} expected = LeftPadBytes(value, 32) assert.Equal(t, expected, output) @@ -321,7 +319,7 @@ func TestEVM(t *testing.T) { bytecode = MustSplice(PUSH32, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, PUSH2, 0x01, 0x00, SHR, return1()) - output, err = call(vm, st, account1, account2, bytecode, nil, &gas) + output, err = call(vm, st, account1, account2, bytecode, nil, gas) value = []byte{0x00} expected = LeftPadBytes(value, 32) assert.Equal(t, expected, output) @@ -336,7 +334,7 @@ func TestEVM(t *testing.T) { bytecode = MustSplice(PUSH32, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, PUSH2, 0x01, 0x00, SHR, return1()) - output, err = call(vm, st, account1, account2, bytecode, nil, &gas) + output, err = call(vm, st, account1, account2, bytecode, nil, gas) value = []byte{0x00} expected = LeftPadBytes(value, 32) assert.Equal(t, expected, output) @@ -351,7 +349,7 @@ func TestEVM(t *testing.T) { bytecode = MustSplice(PUSH32, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, PUSH2, 0x01, 0x01, SHR, return1()) - output, err = call(vm, st, account1, account2, bytecode, nil, &gas) + output, err = call(vm, st, account1, account2, bytecode, nil, gas) value = []byte{0x00} expected = LeftPadBytes(value, 32) assert.Equal(t, expected, output) @@ -368,11 +366,11 @@ func TestEVM(t *testing.T) { account1 := newAccount(t, st, "1") account2 := newAccount(t, st, "101") - var gas uint64 = 100000 + gas := big.NewInt(100000) //Shift arith right 0 bytecode := MustSplice(PUSH1, 0x01, PUSH1, 0x00, SAR, return1()) - output, err := call(vm, st, account1, account2, bytecode, nil, &gas) + output, err := call(vm, st, account1, account2, bytecode, nil, gas) value := []byte{0x1} expected := LeftPadBytes(value, 32) assert.Equal(t, expected, output) @@ -386,7 +384,7 @@ func TestEVM(t *testing.T) { //Alternative arith shift right 0 bytecode = MustSplice(PUSH32, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, PUSH1, 0x00, SAR, return1()) - output, err = call(vm, st, account1, account2, bytecode, nil, &gas) + output, err = call(vm, st, account1, account2, bytecode, nil, gas) expected = []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} @@ -400,7 +398,7 @@ func TestEVM(t *testing.T) { //Shift arith right 1 bytecode = MustSplice(PUSH1, 0x01, PUSH1, 0x01, SAR, return1()) - output, err = call(vm, st, account1, account2, bytecode, nil, &gas) + output, err = call(vm, st, account1, account2, bytecode, nil, gas) value = []byte{0x00} expected = LeftPadBytes(value, 32) assert.Equal(t, expected, output) @@ -414,7 +412,7 @@ func TestEVM(t *testing.T) { //Alternative shift arith right 1 bytecode = MustSplice(PUSH32, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, PUSH1, 0x01, SAR, return1()) - output, err = call(vm, st, account1, account2, bytecode, nil, &gas) + output, err = call(vm, st, account1, account2, bytecode, nil, gas) value = []byte{0xc0} expected = RightPadBytes(value, 32) assert.Equal(t, expected, output) @@ -428,7 +426,7 @@ func TestEVM(t *testing.T) { //Alternative shift arith right 1 bytecode = MustSplice(PUSH32, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, PUSH1, 0x01, SAR, return1()) - output, err = call(vm, st, account1, account2, bytecode, nil, &gas) + output, err = call(vm, st, account1, account2, bytecode, nil, gas) expected = []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} @@ -443,7 +441,7 @@ func TestEVM(t *testing.T) { //Shift arith right 255 bytecode = MustSplice(PUSH32, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, PUSH1, 0xFF, SAR, return1()) - output, err = call(vm, st, account1, account2, bytecode, nil, &gas) + output, err = call(vm, st, account1, account2, bytecode, nil, gas) expected = []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} @@ -458,7 +456,7 @@ func TestEVM(t *testing.T) { //Alternative shift arith right 255 bytecode = MustSplice(PUSH32, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, PUSH1, 0xFF, SAR, return1()) - output, err = call(vm, st, account1, account2, bytecode, nil, &gas) + output, err = call(vm, st, account1, account2, bytecode, nil, gas) expected = []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} @@ -473,7 +471,7 @@ func TestEVM(t *testing.T) { //Alternative shift arith right 255 bytecode = MustSplice(PUSH32, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, PUSH1, 0xFF, SAR, return1()) - output, err = call(vm, st, account1, account2, bytecode, nil, &gas) + output, err = call(vm, st, account1, account2, bytecode, nil, gas) value = []uint8([]byte{0x00}) expected = RightPadBytes(value, 32) assert.Equal(t, expected, output) @@ -488,7 +486,7 @@ func TestEVM(t *testing.T) { bytecode = MustSplice(PUSH32, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, PUSH2, 0x01, 0x00, SAR, return1()) - output, err = call(vm, st, account1, account2, bytecode, nil, &gas) + output, err = call(vm, st, account1, account2, bytecode, nil, gas) expected = []uint8([]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}) @@ -504,7 +502,7 @@ func TestEVM(t *testing.T) { bytecode = MustSplice(PUSH32, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, PUSH2, 0x01, 0x00, SAR, return1()) - output, err = call(vm, st, account1, account2, bytecode, nil, &gas) + output, err = call(vm, st, account1, account2, bytecode, nil, gas) value = []byte{0x00} expected = LeftPadBytes(value, 32) assert.Equal(t, expected, output) @@ -519,7 +517,7 @@ func TestEVM(t *testing.T) { bytecode = MustSplice(PUSH32, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, PUSH2, 0x01, 0x01, SAR, return1()) - output, err = call(vm, st, account1, account2, bytecode, nil, &gas) + output, err = call(vm, st, account1, account2, bytecode, nil, gas) expected = []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} @@ -540,14 +538,14 @@ func TestEVM(t *testing.T) { account1 := newAccount(t, st, "1") account2 := newAccount(t, st, "2") - var gas uint64 = 100000 + gas := big.NewInt(100000) bytecode := MustSplice(PUSH1, 0x10, JUMP) var err error ch := make(chan struct{}) go func() { - _, err = call(vm, st, account1, account2, bytecode, nil, &gas) + _, err = call(vm, st, account1, account2, bytecode, nil, gas) ch <- struct{}{} }() tick := time.NewTicker(time.Second * 2) @@ -568,7 +566,7 @@ func TestEVM(t *testing.T) { account1 := newAccount(t, st, "1, 2, 3") account2 := newAccount(t, st, "3, 2, 1") - var gas uint64 = 1000 + gas := big.NewInt(1000) bytecode := MustSplice(PUSH3, 0x0F, 0x42, 0x40, CALLER, SSTORE, PUSH29, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -585,7 +583,7 @@ func TestEVM(t *testing.T) { JUMPDEST, POP, JUMPDEST, PUSH1, 0x00, PUSH1, 0x00, RETURN) data := hex.MustDecodeString("693200CE0000000000000000000000004B4363CDE27C2EB05E66357DB05BC5C88F850C1A0000000000000000000000000000000000000000000000000000000000000005") - output, err := call(vm, st, account1, account2, bytecode, data, &gas) + output, err := call(vm, st, account1, account2, bytecode, data, gas) t.Logf("Output: %v Error: %v\n", output, err) if err != nil { t.Fatal(err) @@ -606,7 +604,7 @@ func TestEVM(t *testing.T) { err := st.SetStorage(account1, LeftPadWord256(key), value) require.NoError(t, err) - var gas uint64 = 100000 + gas := big.NewInt(100000) bytecode := MustSplice(PUSH13, 0x72, 0x65, 0x76, 0x65, 0x72, 0x74, 0x65, 0x64, 0x20, 0x64, 0x61, 0x74, 0x61, PUSH1, 0x00, SSTORE, PUSH32, 0x72, 0x65, 0x76, 0x65, 0x72, 0x74, 0x20, 0x6D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, @@ -617,7 +615,7 @@ func TestEVM(t *testing.T) { 0x67, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, PUSH1, 0x00, MSTORE, PUSH1, 0x0E, PUSH1, 0x00, REVERT)*/ - output, err := call(vm, st, account1, account2, bytecode, nil, &gas) + output, err := call(vm, st, account1, account2, bytecode, nil, gas) assert.Error(t, err, "Expected execution reverted error") storageVal, err := st.GetStorage(account1, LeftPadWord256(key)) @@ -642,20 +640,20 @@ func TestEVM(t *testing.T) { //---------------------------------------------- // account2 has insufficient balance, should fail - txe := runVM(st, account1, account2, contractCode, 100000) - exCalls := txe.ExceptionalCalls() - require.Len(t, exCalls, 1) - require.Equal(t, errors.Codes.InsufficientBalance, errors.GetCode(exCalls[0].Header.Exception)) + //txe := runVM(st, account1, account2, contractCode, 100000) + //exCalls := txe.ExceptionalCalls() + //require.Len(t, exCalls, 1) + //require.Equal(t, errors.Codes.InsufficientBalance, errors.GetCode(exCalls[0].Header.Exception)) //---------------------------------------------- // give account2 sufficient balance, should pass addToBalance(t, st, account2, 100000) - txe = runVM(st, account1, account2, contractCode, 1000) - assert.Nil(t, txe.Exception, "Should have sufficient balance") + //txe = runVM(st, account1, account2, contractCode, 1000) + //assert.Nil(t, txe.Exception, "Should have sufficient balance") //---------------------------------------------- // insufficient gas, should fail - txe = runVM(st, account1, account2, contractCode, 100) + txe := runVM(st, account1, account2, contractCode, 19) assert.NotNil(t, txe.Exception, "Expected insufficient gas error") }) @@ -667,7 +665,7 @@ func TestEVM(t *testing.T) { var inOff, inSize, retOff, retSize byte logDefault := MustSplice(PUSH1, inSize, PUSH1, inOff) - testRecipient := native.AddressFromName("1") + testRecipient := engine.AddressFromName("1") // check all illegal state modifications in child staticcall frame for _, illegalContractCode := range []acm.Bytecode{ MustSplice(PUSH9, "arbitrary", PUSH1, 0x00, SSTORE), @@ -761,9 +759,9 @@ func TestEVM(t *testing.T) { binary.BigEndian.PutUint64(nonce[txs.HashLength:], 1) addr := crypto.NewContractAddress(callee, nonce) - var gas uint64 = 100000 + gas := big.NewInt(100000) caller := newAccount(t, st, "1, 2, 3") - output, err := call(vm, st, caller, callee, code, nil, &gas) + output, err := call(vm, st, caller, callee, code, nil, gas) assert.NoError(t, err, "Should return new address without error") assert.Equal(t, addr.Bytes(), output, "Addresses should be equal") }) @@ -778,9 +776,9 @@ func TestEVM(t *testing.T) { callee := makeAccountWithCode(t, st, "callee", code) addr := crypto.NewContractAddress2(callee, salt, code) - var gas uint64 = 100000 + gas := big.NewInt(100000) caller := newAccount(t, st, "1, 2, 3") - output, err := call(vm, st, caller, callee, code, nil, &gas) + output, err := call(vm, st, caller, callee, code, nil, gas) assert.NoError(t, err, "Should return new address without error") assert.Equal(t, addr.Bytes(), output, "Returned value not equal to create2 address") }) @@ -802,16 +800,13 @@ func TestEVM(t *testing.T) { callee := makeAccountWithCode(t, st, "callee", MustSplice(PUSH1, calleeReturnValue, PUSH1, 0, MSTORE, PUSH1, 32, PUSH1, 0, RETURN)) // 6 op codes total - baseOpsCost := native.GasBaseOp * 6 + baseOpsCost := engine.GasBaseOp * 6 // 4 pushes - pushCost := native.GasStackOp * 4 + pushCost := engine.GasStackOp * 4 // 2 pushes 2 pops - returnCost := native.GasStackOp * 4 - // To push success/failure - resumeCost := native.GasStackOp + returnCost := engine.GasStackOp * 4 - // Gas is not allowed to drop to 0 so we add resumecost - delegateCallCost := baseOpsCost + pushCost + returnCost + resumeCost + delegateCallCost := baseOpsCost + pushCost + returnCost // Here we split up the caller code so we can make a DELEGATE call with // different amounts of gas. The value we sandwich in the middle is the amount @@ -823,15 +818,14 @@ func TestEVM(t *testing.T) { callerCodeSuffix := MustSplice(DELEGATECALL, returnWord()) // Perform a delegate call - //callerCode := MustSplice(callerCodePrefix, - // Give just enough gas to make the DELEGATECALL - //delegateCallCost, callerCodeSuffix) - //caller := makeAccountWithCode(t, st, "caller", callerCode) - + //Give just enough gas to make the DELEGATECALL + callerCode := MustSplice(callerCodePrefix, + delegateCallCost, callerCodeSuffix) + caller := makeAccountWithCode(t, st, "caller", callerCode) // Should pass - //txe := runVM(st, caller, callee, callerCode, 100) - //assert.Nil(t, txe.Exception, "Should have sufficient funds for call") - //assert.Equal(t, Int64ToWord256(calleeReturnValue).Bytes(), txe.Result.Return) + txe := runVM(st, caller, callee, callerCode, 100) + assert.Nil(t, txe.Exception, "Should have sufficient funds for call") + assert.Equal(t, Int64ToWord256(calleeReturnValue).Bytes(), txe.Result.Return) callerCode2 := MustSplice(callerCodePrefix, // Shouldn't be enough gas to make call @@ -839,26 +833,25 @@ func TestEVM(t *testing.T) { caller2 := makeAccountWithCode(t, st, "caller2", callerCode2) // Should fail - txe := runVM(st, caller2, callee, callerCode2, 100) + txe = runVM(st, caller2, callee, callerCode2, 100) assert.NotNil(t, txe.Exception, "Should have insufficient gas for call") }) t.Run("MemoryBounds", func(t *testing.T) { st := acmstate.NewMemoryState() - blockchain := new(blockchain) + blockchain := new(engine.TestBlockchain) eventSink := exec.NewNoopEventSink() vm := New(Options{ - MemoryProvider: func(err errors.Sink) Memory { - return NewDynamicMemory(1024, 2048, err) + MemoryProvider: func(err errors.Sink) engine.Memory { + return engine.NewDynamicMemory(1024, 2048, err) }, }) caller := makeAccountWithCode(t, st, "caller", nil) callee := makeAccountWithCode(t, st, "callee", nil) - gas := uint64(100000) word := One256 // This attempts to store a value at the memory boundary and return it params := engine.CallParams{ - Gas: &gas, + Gas: big.NewInt(100000), Caller: caller, Callee: callee, } @@ -899,7 +892,7 @@ func TestEVM(t *testing.T) { st := acmstate.NewMemoryState() account1 := newAccount(t, st, "1, 2, 3") account2 := newAccount(t, st, "3, 2, 1") - var gas uint64 = 100000 + gas := big.NewInt(100000) /* pragma solidity ^0.5.4; @@ -920,18 +913,18 @@ func TestEVM(t *testing.T) { var err error // Run the contract initialisation code to obtain the contract code that would be mounted at account2 - contractCode, err := call(vm, st, account1, account2, code, code, &gas) + contractCode, err := call(vm, st, account1, account2, code, code, gas) require.NoError(t, err) // Not needed for this test (since contract code is passed as argument to vm), but this is what an execution // framework must do - err = native.InitEVMCode(st, account2, contractCode) + err = engine.InitEVMCode(st, account2, contractCode) require.NoError(t, err) // Input is the function hash of `get()` input := hex.MustDecodeString("6d4ce63c") - output, err := call(vm, st, account1, account2, contractCode, input, &gas) + output, err := call(vm, st, account1, account2, contractCode, input, gas) require.NoError(t, err) assert.Equal(t, account1.Word256().Bytes(), output) @@ -944,13 +937,13 @@ func TestEVM(t *testing.T) { account1 := newAccount(t, st, "1") account2 := newAccount(t, st, "1, 0, 1") - var gas uint64 = 100000 + gas := big.NewInt(100000) bytecode := MustSplice(PUSH32, 0x72, 0x65, 0x76, 0x65, 0x72, 0x74, 0x20, 0x6D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, PUSH1, 0x00, MSTORE, PUSH1, 0x0E, PUSH1, 0x00, INVALID) - output, err := call(vm, st, account1, account2, bytecode, nil, &gas) + output, err := call(vm, st, account1, account2, bytecode, nil, gas) assert.Equal(t, errors.Codes.ExecutionAborted, errors.GetCode(err)) t.Logf("Output: %v Error: %v\n", output, err) }) @@ -967,7 +960,7 @@ func TestEVM(t *testing.T) { account1 := newAccount(t, st, "1") account2 := makeAccountWithCode(t, st, accountName, callcode) - var gas uint64 = 100000 + gas := big.NewInt(100000) gas1, gas2 := byte(0x1), byte(0x1) value := byte(0x69) @@ -980,7 +973,7 @@ func TestEVM(t *testing.T) { expected := Uint64ToWord256(uint64(len(ret))).Bytes() - output, err := call(vm, st, account1, account2, bytecode, nil, &gas) + output, err := call(vm, st, account1, account2, bytecode, nil, gas) require.NoError(t, err) assert.Equal(t, expected, output) @@ -1003,7 +996,7 @@ func TestEVM(t *testing.T) { account1 := newAccount(t, st, "1") account2 := makeAccountWithCode(t, st, accountName, callcode) - var gas uint64 = 100000 + gas := big.NewInt(100000) gas1, gas2 := byte(0x1), byte(0x1) value := byte(0x69) @@ -1016,7 +1009,7 @@ func TestEVM(t *testing.T) { expected := []byte(ret) - output, err := call(vm, st, account1, account2, bytecode, nil, &gas) + output, err := call(vm, st, account1, account2, bytecode, nil, gas) require.NoError(t, err) assert.Equal(t, expected, output) @@ -1025,18 +1018,17 @@ func TestEVM(t *testing.T) { t.Run("CallNonExistent", func(t *testing.T) { st := acmstate.NewMemoryState() - blockchain := new(blockchain) + blockchain := new(engine.TestBlockchain) eventSink := exec.NewNoopEventSink() account1 := newAccount(t, st, "1") addToBalance(t, st, account1, 100000) - unknownAddress := native.AddressFromName("nonexistent") - var gas uint64 - amt := uint64(100) + unknownAddress := engine.AddressFromName("nonexistent") + params := engine.CallParams{ Caller: account1, Callee: unknownAddress, - Value: amt, - Gas: &gas, + Value: *big.NewInt(100), + Gas: big.NewInt(100000), } _, ex := vm.Execute(st, blockchain, eventSink, params, nil) require.Equal(t, errors.Codes.NonExistentAccount, errors.GetCode(ex), @@ -1048,30 +1040,30 @@ func TestEVM(t *testing.T) { t.Run("GetBlockHash", func(t *testing.T) { st := acmstate.NewMemoryState() - blockchain := new(blockchain) + blockchain := new(engine.TestBlockchain) eventSink := exec.NewNoopEventSink() // Create accounts account1 := newAccount(t, st, "1") account2 := newAccount(t, st, "101") - var gas uint64 = 100000 + var gas = big.NewInt(100000) bytecode := MustSplice(PUSH1, 2, BLOCKHASH) params := engine.CallParams{ Caller: account1, Callee: account2, - Gas: &gas, + Gas: gas, } // Non existing block - blockchain.blockHeight = 1 + blockchain.BlockHeight = 1 _, err := vm.Execute(st, blockchain, eventSink, params, bytecode) require.Equal(t, errors.Codes.InvalidBlockNumber, errors.GetCode(err), "attempt to get block hash of a non-existent block") // Excessive old block - blockchain.blockHeight = 258 + blockchain.BlockHeight = 258 bytecode = MustSplice(PUSH1, 1, BLOCKHASH) _, err = vm.Execute(st, blockchain, eventSink, params, bytecode) @@ -1079,7 +1071,7 @@ func TestEVM(t *testing.T) { "attempt to get block hash of a block outside of allowed range") // Get block hash - blockchain.blockHeight = 257 + blockchain.BlockHeight = 257 bytecode = MustSplice(PUSH1, 2, BLOCKHASH, return1()) output, err := vm.Execute(st, blockchain, eventSink, params, bytecode) @@ -1087,7 +1079,7 @@ func TestEVM(t *testing.T) { assert.Equal(t, LeftPadWord256([]byte{2}), LeftPadWord256(output)) // Get block hash fail - blockchain.blockHeight = 3 + blockchain.BlockHeight = 3 bytecode = MustSplice(PUSH1, 4, BLOCKHASH, return1()) _, err = vm.Execute(st, blockchain, eventSink, params, bytecode) @@ -1176,13 +1168,11 @@ func TestEVM(t *testing.T) { t.Run("DataStackOverflow", func(t *testing.T) { st := acmstate.NewMemoryState() - blockchain := new(blockchain) + blockchain := new(engine.TestBlockchain) eventSink := exec.NewNoopEventSink() account1 := newAccount(t, st, "1, 2, 3") account2 := newAccount(t, st, "3, 2, 1") - var gas uint64 = 100000 - /* pragma solidity ^0.5.4; @@ -1207,7 +1197,7 @@ func TestEVM(t *testing.T) { Caller: account1, Callee: account2, Input: code, - Gas: &gas, + Gas: big.NewInt(1000000), } vm := New(Options{ DataStackMaxDepth: 4, @@ -1226,7 +1216,7 @@ func TestEVM(t *testing.T) { t.Run("CallStackOverflow", func(t *testing.T) { st := acmstate.NewMemoryState() - blockchain := new(blockchain) + blockchain := new(engine.TestBlockchain) eventSink := exec.NewNoopEventSink() txe := new(exec.TxExecution) @@ -1234,7 +1224,6 @@ func TestEVM(t *testing.T) { account2 := newAccount(t, st, "3, 2, 1") // Sender accepts lot of gaz but we run on a caped call stack node - var gas uint64 = 100000 /* pragma solidity ^0.5.4; @@ -1263,7 +1252,7 @@ func TestEVM(t *testing.T) { Caller: account1, Callee: account2, Input: code, - Gas: &gas, + Gas: big.NewInt(1000000), } options := Options{ CallStackMaxDepth: 2, @@ -1273,9 +1262,9 @@ func TestEVM(t *testing.T) { contractCode, err := vm.Execute(st, blockchain, eventSink, params, code) require.NoError(t, err) - err = native.InitEVMCode(st, account1, contractCode) + err = engine.InitEVMCode(st, account1, contractCode) require.NoError(t, err) - err = native.InitEVMCode(st, account2, contractCode) + err = engine.InitEVMCode(st, account2, contractCode) require.NoError(t, err) // keccak256 hash of 'callMeBack()' @@ -1288,7 +1277,7 @@ func TestEVM(t *testing.T) { require.Error(t, err) callError := txe.CallError() require.Error(t, callError) - require.Equal(t, errors.Codes.ExecutionReverted, errors.GetCode(callError)) + require.Equal(t, errors.Codes.CallStackOverflow, errors.GetCode(callError)) // Errors are post-order so first is deepest require.True(t, len(callError.NestedErrors) > 0) deepestErr := callError.NestedErrors[0] @@ -1303,31 +1292,31 @@ func TestEVM(t *testing.T) { account1 := newAccount(t, st, "1") account2 := newAccount(t, st, "101") - var gas uint64 = 100000 + gas := big.NewInt(100000) // The EXTCODEHASH of the account without code is c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 // what is the keccack256 hash of empty data. bytecode := MustSplice(PUSH20, account1, EXTCODEHASH, return1()) - output, err := call(vm, st, account1, account2, bytecode, nil, &gas) + output, err := call(vm, st, account1, account2, bytecode, nil, gas) assert.NoError(t, err) assert.Equal(t, hex.MustDecodeString("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"), output) // The EXTCODEHASH of a native account is hash of its name. bytecode = MustSplice(PUSH1, 0x03, EXTCODEHASH, return1()) - output, err = call(vm, st, account1, account2, bytecode, nil, &gas) + output, err = call(vm, st, account1, account2, bytecode, nil, gas) assert.NoError(t, err) assert.Equal(t, crypto.Keccak256([]byte("ripemd160Func")), output) // EXTCODEHASH of non-existent account should be 0 bytecode = MustSplice(PUSH1, 0xff, EXTCODEHASH, return1()) - output, err = call(vm, st, account1, account2, bytecode, nil, &gas) + output, err = call(vm, st, account1, account2, bytecode, nil, gas) assert.NoError(t, err) assert.Equal(t, Zero256[:], output) // EXTCODEHASH is the hash of an account code acc := makeAccountWithCode(t, st, "trustedCode", MustSplice(BLOCKHASH, return1())) bytecode = MustSplice(PUSH20, acc, EXTCODEHASH, return1()) - output, err = call(vm, st, account1, account2, bytecode, nil, &gas) + output, err = call(vm, st, account1, account2, bytecode, nil, gas) assert.NoError(t, err) assert.Equal(t, hex.MustDecodeString("010da270094b5199d3e54f89afe4c66cdd658dd8111a41998714227e14e171bd"), output) }) @@ -1343,18 +1332,16 @@ func TestEVM(t *testing.T) { Int64ToWord256(4)} st := acmstate.NewMemoryState() - blockchain := new(blockchain) + blockchain := new(engine.TestBlockchain) // Create accounts address1 := crypto.Address{1, 3, 5, 7, 9} address2 := crypto.Address{2, 4, 6, 8, 10} - err := native.CreateAccount(st, address1) + err := engine.CreateAccount(st, address1) require.NoError(t, err) - err = native.CreateAccount(st, address2) + err = engine.CreateAccount(st, address2) require.NoError(t, err) - var gas uint64 = 100000 - mstore8 := byte(MSTORE8) push1 := byte(PUSH1) log4 := byte(LOG4) @@ -1379,7 +1366,7 @@ func TestEVM(t *testing.T) { params := engine.CallParams{ Caller: address1, Callee: address2, - Gas: &gas, + Gas: big.NewInt(1000000), } _, err = vm.Execute(st, blockchain, txe, params, code) require.NoError(t, err) @@ -1432,8 +1419,8 @@ func TestEVM(t *testing.T) { BigIntToWord256(b), BigIntToWord256(e), BigIntToWord256(m), // base^exp % mod BigIntToWord256(v)) // == expected - gas := uint64(10000000) - out, err := call(vm, st, account1, account2, bytecode, input, &gas) + gas := big.NewInt(10000000) + out, err := call(vm, st, account1, account2, bytecode, input, gas) require.NoError(t, err) @@ -1446,12 +1433,11 @@ func TestEVM(t *testing.T) { t.Run("SDIV", func(t *testing.T) { st := acmstate.NewMemoryState() - blockchain := new(blockchain) + blockchain := new(engine.TestBlockchain) eventSink := exec.NewNoopEventSink() account1 := newAccount(t, st, "1") account2 := newAccount(t, st, "101") - var gas uint64 = 100000 tests := []struct { name string @@ -1499,7 +1485,7 @@ func TestEVM(t *testing.T) { params := engine.CallParams{ Caller: account1, Callee: account2, - Gas: &gas, + Gas: big.NewInt(1000000), } if output, err := vm.Execute(st, blockchain, eventSink, params, tt.bytecode); err != nil || !bytes.Equal(output, tt.expected) { t.Errorf("Reported error in %v.", tt.name) @@ -1511,12 +1497,11 @@ func TestEVM(t *testing.T) { t.Run("SMOD", func(t *testing.T) { st := acmstate.NewMemoryState() - blockchain := new(blockchain) + blockchain := new(engine.TestBlockchain) eventSink := exec.NewNoopEventSink() account1 := newAccount(t, st, "1") account2 := newAccount(t, st, "101") - var gas uint64 = 100000 tests := []struct { name string @@ -1564,7 +1549,7 @@ func TestEVM(t *testing.T) { params := engine.CallParams{ Caller: account1, Callee: account2, - Gas: &gas, + Gas: big.NewInt(1000000), } if output, err := vm.Execute(st, blockchain, eventSink, params, tt.bytecode); err != nil || !bytes.Equal(output, tt.expected) { t.Errorf("Reported error in %v.", tt.name) @@ -1576,12 +1561,11 @@ func TestEVM(t *testing.T) { t.Run("SIGNEXTEND and MSTORE8", func(t *testing.T) { st := acmstate.NewMemoryState() - blockchain := new(blockchain) + blockchain := new(engine.TestBlockchain) eventSink := exec.NewNoopEventSink() account1 := newAccount(t, st, "1") account2 := newAccount(t, st, "101") - var gas uint64 = 100000 tests := []struct { name string @@ -1629,7 +1613,7 @@ func TestEVM(t *testing.T) { params := engine.CallParams{ Caller: account1, Callee: account2, - Gas: &gas, + Gas: big.NewInt(1000000), } if output, err := vm.Execute(st, blockchain, eventSink, params, tt.bytecode); err != nil || !bytes.Equal(output, tt.expected) { t.Errorf("Reported error in %v.", tt.name) @@ -1641,12 +1625,11 @@ func TestEVM(t *testing.T) { t.Run("JUMPDEST", func(t *testing.T) { st := acmstate.NewMemoryState() - blockchain := new(blockchain) + blockchain := new(engine.TestBlockchain) eventSink := exec.NewNoopEventSink() account1 := newAccount(t, st, "1") account2 := newAccount(t, st, "101") - var gas uint64 = 100000 tests := []struct { name string @@ -1671,7 +1654,7 @@ func TestEVM(t *testing.T) { params := engine.CallParams{ Caller: account1, Callee: account2, - Gas: &gas, + Gas: big.NewInt(1000000), } if output, err := vm.Execute(st, blockchain, eventSink, params, tt.bytecode); errors.GetCode(err) != tt.expected_err { t.Errorf("FAIL: %v.", tt.name) @@ -1684,59 +1667,37 @@ func TestEVM(t *testing.T) { }) } -type blockchain struct { - blockHeight uint64 - blockTime time.Time -} - -func (b *blockchain) LastBlockHeight() uint64 { - return b.blockHeight -} - -func (b *blockchain) LastBlockTime() time.Time { - return b.blockTime -} - -func (b *blockchain) BlockHash(height uint64) ([]byte, error) { - if height > b.blockHeight { - return nil, errors.Codes.InvalidBlockNumber - } - bs := make([]byte, 32) - binary.BigEndian.PutUint64(bs[24:], height) - return bs, nil -} - // helpers func newAccount(t testing.TB, st acmstate.ReaderWriter, name string) crypto.Address { - address := native.AddressFromName(name) - err := native.CreateAccount(st, address) + address := engine.AddressFromName(name) + err := engine.CreateAccount(st, address) require.NoError(t, err) return address } func makeAccountWithCode(t testing.TB, st acmstate.ReaderWriter, name string, code []byte) crypto.Address { - address := native.AddressFromName(name) - err := native.CreateAccount(st, address) + address := engine.AddressFromName(name) + err := engine.CreateAccount(st, address) require.NoError(t, err) - err = native.InitEVMCode(st, address, code) + err = engine.InitEVMCode(st, address, code) require.NoError(t, err) addToBalance(t, st, address, 9999999) return address } func addToBalance(t testing.TB, st acmstate.ReaderWriter, address crypto.Address, amount uint64) { - err := native.UpdateAccount(st, address, func(account *acm.Account) error { + err := engine.UpdateAccount(st, address, func(account *acm.Account) error { return account.AddToBalance(amount) }) require.NoError(t, err) } func call(vm *EVM, st acmstate.ReaderWriter, origin, callee crypto.Address, code []byte, input []byte, - gas *uint64) ([]byte, error) { + gas *big.Int) ([]byte, error) { evs := new(exec.Events) - out, err := vm.Execute(st, new(blockchain), evs, engine.CallParams{ + out, err := vm.Execute(st, new(engine.TestBlockchain), evs, engine.CallParams{ Caller: origin, Callee: callee, Input: input, @@ -1783,22 +1744,23 @@ func returnWord() []byte { // event (in the case of no direct error from call we will block waiting for // at least 1 AccCall event) func runVM(st acmstate.ReaderWriter, caller, callee crypto.Address, code []byte, gas uint64) *exec.TxExecution { - gasBefore := gas + gasBefore := new(big.Int).SetUint64(gas) txe := new(exec.TxExecution) vm := New(Options{ DebugOpcodes: true, }) + bigGas := new(big.Int).SetUint64(gas) params := engine.CallParams{ Caller: caller, Callee: callee, - Gas: &gas, + Gas: bigGas, } - output, err := vm.Execute(st, new(blockchain), txe, params, code) + output, err := vm.Execute(st, new(engine.TestBlockchain), txe, params, code) txe.PushError(err) for _, ev := range txe.ExceptionalCalls() { txe.PushError(ev.Header.Exception) } - txe.Return(output, gasBefore-gas) + txe.Return(output, gasBefore.Sub(gasBefore, bigGas).Uint64()) return txe } diff --git a/execution/evm/precompiles_test.go b/execution/evm/precompiles_test.go index fbc3c51b1..180ead428 100644 --- a/execution/evm/precompiles_test.go +++ b/execution/evm/precompiles_test.go @@ -4,6 +4,7 @@ package evm import ( + "math/big" "testing" "github.com/hyperledger/burrow/execution/evm/abi" @@ -47,8 +48,6 @@ func TestECRecover(t *testing.T) { EventSink: exec.NewNoopEventSink(), } - gas := uint64(10000) - spec, err := abi.ReadSpec(solidity.Abi_ECRecover) require.NoError(t, err) funcId := spec.Functions["recoverSigningAddress"].FunctionID @@ -57,7 +56,7 @@ func TestECRecover(t *testing.T) { params := engine.CallParams{ Caller: caller.Address, Input: input, - Gas: &gas, + Gas: big.NewInt(10000), } vm := New(Options{ diff --git a/execution/evm/stack.go b/execution/evm/stack.go index 55c5baf6a..a8e29ff35 100644 --- a/execution/evm/stack.go +++ b/execution/evm/stack.go @@ -8,10 +8,11 @@ import ( "math" "math/big" + "github.com/hyperledger/burrow/execution/engine" + . "github.com/hyperledger/burrow/binary" "github.com/hyperledger/burrow/crypto" "github.com/hyperledger/burrow/execution/errors" - "github.com/hyperledger/burrow/execution/native" ) // Not goroutine safe @@ -20,11 +21,11 @@ type Stack struct { maxCapacity uint64 ptr int - gas *uint64 + gas *big.Int errSink errors.Sink } -func NewStack(errSink errors.Sink, initialCapacity uint64, maxCapacity uint64, gas *uint64) *Stack { +func NewStack(errSink errors.Sink, initialCapacity uint64, maxCapacity uint64, gas *big.Int) *Stack { return &Stack{ slice: make([]Word256, initialCapacity), ptr: 0, @@ -35,7 +36,7 @@ func NewStack(errSink errors.Sink, initialCapacity uint64, maxCapacity uint64, g } func (st *Stack) Push(d Word256) { - st.useGas(native.GasStackOp) + st.useGas(engine.GasStackOp) err := st.ensureCapacity(uint64(st.ptr) + 1) if err != nil { st.pushErr(errors.Codes.DataStackOverflow) @@ -46,7 +47,7 @@ func (st *Stack) Push(d Word256) { } func (st *Stack) Pop() Word256 { - st.useGas(native.GasStackOp) + st.useGas(engine.GasStackOp) if st.ptr == 0 { st.pushErr(errors.Codes.DataStackUnderflow) return Zero256 @@ -109,7 +110,7 @@ func (st *Stack) Len() int { } func (st *Stack) Swap(n int) { - st.useGas(native.GasStackOp) + st.useGas(engine.GasStackOp) if st.ptr < n { st.pushErr(errors.Codes.DataStackUnderflow) return @@ -118,7 +119,7 @@ func (st *Stack) Swap(n int) { } func (st *Stack) Dup(n int) { - st.useGas(native.GasStackOp) + st.useGas(engine.GasStackOp) if st.ptr < n { st.pushErr(errors.Codes.DataStackUnderflow) return @@ -197,11 +198,7 @@ func (st *Stack) ensureCapacity(newCapacity uint64) error { } func (st *Stack) useGas(gasToUse uint64) { - if *st.gas > gasToUse { - *st.gas -= gasToUse - } else { - st.pushErr(errors.Codes.InsufficientGas) - } + st.pushErr(engine.UseGasNegative(st.gas, gasToUse)) } func (st *Stack) pushErr(err errors.CodedError) { diff --git a/execution/evm/stack_test.go b/execution/evm/stack_test.go index f184f00b7..abd673758 100644 --- a/execution/evm/stack_test.go +++ b/execution/evm/stack_test.go @@ -2,6 +2,7 @@ package evm import ( "math" + "math/big" "testing" "github.com/hyperledger/burrow/binary" @@ -10,9 +11,10 @@ import ( "github.com/stretchr/testify/require" ) +var maxUint64 = big.NewInt(math.MaxInt64) + func TestStack_MaxDepthInt32(t *testing.T) { - var gaz uint64 = math.MaxUint64 - st := NewStack(new(errors.Maybe), 0, 0, &gaz) + st := NewStack(new(errors.Maybe), 0, 0, maxUint64) err := st.ensureCapacity(math.MaxInt32 + 1) assert.Error(t, err) @@ -21,8 +23,7 @@ func TestStack_MaxDepthInt32(t *testing.T) { // Test static memory allocation with unlimited depth - memory should grow func TestStack_UnlimitedAllocation(t *testing.T) { err := new(errors.Maybe) - var gaz uint64 = math.MaxUint64 - st := NewStack(err, 0, 0, &gaz) + st := NewStack(err, 0, 0, maxUint64) st.Push64(math.MaxInt64) require.NoError(t, err.Error()) @@ -33,8 +34,8 @@ func TestStack_UnlimitedAllocation(t *testing.T) { // Test static memory allocation with maximum == initial capacity - memory should not grow func TestStack_StaticAllocation(t *testing.T) { err := new(errors.Maybe) - var gaz uint64 = math.MaxUint64 - st := NewStack(err, 4, 4, &gaz) + + st := NewStack(err, 4, 4, maxUint64) for i := 0; i < 4; i++ { st.Push64(math.MaxInt64) @@ -47,8 +48,8 @@ func TestStack_StaticAllocation(t *testing.T) { // Test writing beyond the current capacity - memory should grow func TestDynamicMemory_PushAhead(t *testing.T) { err := new(errors.Maybe) - var gaz uint64 = math.MaxUint64 - st := NewStack(err, 2, 4, &gaz) + + st := NewStack(err, 2, 4, maxUint64) for i := 0; i < 4; i++ { st.Push64(math.MaxInt64) @@ -61,16 +62,16 @@ func TestDynamicMemory_PushAhead(t *testing.T) { func TestStack_ZeroInitialCapacity(t *testing.T) { err := new(errors.Maybe) - var gaz uint64 = math.MaxUint64 - st := NewStack(err, 0, 16, &gaz) + + st := NewStack(err, 0, 16, maxUint64) require.NoError(t, err.Error()) st.Push64(math.MaxInt64) assert.Equal(t, []binary.Word256{binary.Int64ToWord256(math.MaxInt64)}, st.slice) } func TestStack_ensureCapacity(t *testing.T) { - var gaz uint64 = math.MaxUint64 - st := NewStack(new(errors.Maybe), 4, 16, &gaz) + + st := NewStack(new(errors.Maybe), 4, 16, maxUint64) // Check we can grow within bounds err := st.ensureCapacity(8) assert.NoError(t, err) diff --git a/execution/exec/call_event.go b/execution/exec/call_event.go index 64b721810..63ab6eba2 100644 --- a/execution/exec/call_event.go +++ b/execution/exec/call_event.go @@ -3,6 +3,7 @@ package exec type CallType uint32 const ( + CallTypeInvalid = CallType(1<<32 - 1) CallTypeCall = CallType(0x00) CallTypeCode = CallType(0x01) CallTypeDelegate = CallType(0x02) @@ -10,6 +11,7 @@ const ( ) var nameFromCallType = map[CallType]string{ + CallTypeInvalid: "InvalidCall", CallTypeCall: "Call", CallTypeCode: "CallCode", CallTypeDelegate: "DelegateCall", diff --git a/execution/exec/exec.pb.go b/execution/exec/exec.pb.go index 94721a277..fc390f79d 100644 --- a/execution/exec/exec.pb.go +++ b/execution/exec/exec.pb.go @@ -1264,14 +1264,15 @@ func (*OutputEvent) XXX_MessageName() string { } type CallData struct { - Caller github_com_hyperledger_burrow_crypto.Address `protobuf:"bytes,1,opt,name=Caller,proto3,customtype=github.com/hyperledger/burrow/crypto.Address" json:"Caller"` - Callee github_com_hyperledger_burrow_crypto.Address `protobuf:"bytes,2,opt,name=Callee,proto3,customtype=github.com/hyperledger/burrow/crypto.Address" json:"Callee"` - Data github_com_hyperledger_burrow_binary.HexBytes `protobuf:"bytes,3,opt,name=Data,proto3,customtype=github.com/hyperledger/burrow/binary.HexBytes" json:"Data"` - Value uint64 `protobuf:"varint,4,opt,name=Value,proto3" json:"Value,omitempty"` - Gas uint64 `protobuf:"varint,5,opt,name=Gas,proto3" json:"Gas,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + Caller github_com_hyperledger_burrow_crypto.Address `protobuf:"bytes,1,opt,name=Caller,proto3,customtype=github.com/hyperledger/burrow/crypto.Address" json:"Caller"` + Callee github_com_hyperledger_burrow_crypto.Address `protobuf:"bytes,2,opt,name=Callee,proto3,customtype=github.com/hyperledger/burrow/crypto.Address" json:"Callee"` + Data github_com_hyperledger_burrow_binary.HexBytes `protobuf:"bytes,3,opt,name=Data,proto3,customtype=github.com/hyperledger/burrow/binary.HexBytes" json:"Data"` + // Bytes of a big integer value + Value []byte `protobuf:"bytes,4,opt,name=Value,proto3" json:"Value,omitempty"` + Gas []byte `protobuf:"bytes,5,opt,name=Gas,proto3" json:"Gas,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *CallData) Reset() { *m = CallData{} } @@ -1303,18 +1304,18 @@ func (m *CallData) XXX_DiscardUnknown() { var xxx_messageInfo_CallData proto.InternalMessageInfo -func (m *CallData) GetValue() uint64 { +func (m *CallData) GetValue() []byte { if m != nil { return m.Value } - return 0 + return nil } -func (m *CallData) GetGas() uint64 { +func (m *CallData) GetGas() []byte { if m != nil { return m.Gas } - return 0 + return nil } func (*CallData) XXX_MessageName() string { @@ -1367,7 +1368,7 @@ func init() { proto.RegisterFile("exec.proto", fileDescriptor_4d737c7315c25422) func init() { golang_proto.RegisterFile("exec.proto", fileDescriptor_4d737c7315c25422) } var fileDescriptor_4d737c7315c25422 = []byte{ - // 1311 bytes of a gzipped FileDescriptorProto + // 1314 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x57, 0xcf, 0x6f, 0x1b, 0xc5, 0x17, 0xef, 0xda, 0x6b, 0xc7, 0x7e, 0x76, 0xfa, 0x6d, 0x47, 0xf9, 0x22, 0xab, 0xaa, 0xec, 0xb2, 0x45, 0xa5, 0x94, 0xb2, 0xae, 0x02, 0x45, 0xa8, 0x48, 0x88, 0xba, 0x09, 0x6d, 0xa0, 0xa4, 0x65, @@ -1445,11 +1446,12 @@ var fileDescriptor_4d737c7315c25422 = []byte{ 0xff, 0x9f, 0x0c, 0xd9, 0x24, 0x1a, 0x3b, 0x9c, 0x21, 0x84, 0x96, 0xb1, 0xd6, 0x67, 0x00, 0xf3, 0xa9, 0x3e, 0x6c, 0xaa, 0x59, 0x9f, 0x43, 0xab, 0xb0, 0x0a, 0x0e, 0xdd, 0xfd, 0x37, 0x15, 0x28, 0x75, 0x56, 0x9c, 0x71, 0xb1, 0xfd, 0xe3, 0xce, 0x2a, 0x1f, 0xb9, 0x37, 0x76, 0x30, 0x9e, 0x28, - 0x1f, 0xf9, 0xc8, 0x55, 0x0f, 0x3e, 0x72, 0x4b, 0x50, 0xbb, 0xe3, 0x8c, 0x53, 0xa6, 0x1f, 0x97, - 0x52, 0x20, 0xc7, 0xa0, 0x7a, 0xd5, 0xd1, 0x2f, 0x7f, 0x71, 0x1c, 0xbc, 0xb7, 0xb5, 0xdd, 0x35, - 0x1e, 0x6d, 0x77, 0x8d, 0x5f, 0xb6, 0xbb, 0xc6, 0x6f, 0xdb, 0x5d, 0xe3, 0xa7, 0x27, 0x5d, 0x63, - 0xeb, 0x49, 0xd7, 0xf8, 0x74, 0x9f, 0x14, 0x98, 0x7e, 0x14, 0xc8, 0xd3, 0x46, 0x5d, 0xde, 0xd7, - 0xaf, 0xff, 0x15, 0x00, 0x00, 0xff, 0xff, 0xf2, 0xc4, 0xac, 0x30, 0x8d, 0x0f, 0x00, 0x00, + 0x1f, 0xf9, 0xc8, 0x55, 0x0f, 0x3e, 0x72, 0x4b, 0x50, 0xbb, 0xe3, 0x8c, 0x53, 0x75, 0xad, 0xb6, + 0xa9, 0x12, 0xc8, 0x31, 0xa8, 0x5e, 0x75, 0xd4, 0xcb, 0xbf, 0x4d, 0xc5, 0x71, 0xf0, 0xde, 0xd6, + 0x76, 0xd7, 0x78, 0xb4, 0xdd, 0x35, 0x7e, 0xd9, 0xee, 0x1a, 0xbf, 0x6d, 0x77, 0x8d, 0x9f, 0x9e, + 0x74, 0x8d, 0xad, 0x27, 0x5d, 0xe3, 0xd3, 0x7d, 0x52, 0x60, 0xfa, 0x51, 0x20, 0x4f, 0x1b, 0x75, + 0x79, 0x5f, 0xbf, 0xfe, 0x57, 0x00, 0x00, 0x00, 0xff, 0xff, 0xde, 0x53, 0x6b, 0x0d, 0x8d, 0x0f, + 0x00, 0x00, } func (m *StreamEvents) Marshal() (dAtA []byte, err error) { @@ -2619,15 +2621,19 @@ func (m *CallData) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } - if m.Gas != 0 { - i = encodeVarintExec(dAtA, i, uint64(m.Gas)) + if len(m.Gas) > 0 { + i -= len(m.Gas) + copy(dAtA[i:], m.Gas) + i = encodeVarintExec(dAtA, i, uint64(len(m.Gas))) i-- - dAtA[i] = 0x28 + dAtA[i] = 0x2a } - if m.Value != 0 { - i = encodeVarintExec(dAtA, i, uint64(m.Value)) + if len(m.Value) > 0 { + i -= len(m.Value) + copy(dAtA[i:], m.Value) + i = encodeVarintExec(dAtA, i, uint64(len(m.Value))) i-- - dAtA[i] = 0x20 + dAtA[i] = 0x22 } { size := m.Data.Size() @@ -3150,11 +3156,13 @@ func (m *CallData) Size() (n int) { n += 1 + l + sovExec(uint64(l)) l = m.Data.Size() n += 1 + l + sovExec(uint64(l)) - if m.Value != 0 { - n += 1 + sovExec(uint64(m.Value)) + l = len(m.Value) + if l > 0 { + n += 1 + l + sovExec(uint64(l)) } - if m.Gas != 0 { - n += 1 + sovExec(uint64(m.Gas)) + l = len(m.Gas) + if l > 0 { + n += 1 + l + sovExec(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) @@ -6359,10 +6367,10 @@ func (m *CallData) Unmarshal(dAtA []byte) error { } iNdEx = postIndex case 4: - if wireType != 0 { + if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType) } - m.Value = 0 + var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowExec @@ -6372,16 +6380,31 @@ func (m *CallData) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Value |= uint64(b&0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } + if byteLen < 0 { + return ErrInvalidLengthExec + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthExec + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Value = append(m.Value[:0], dAtA[iNdEx:postIndex]...) + if m.Value == nil { + m.Value = []byte{} + } + iNdEx = postIndex case 5: - if wireType != 0 { + if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Gas", wireType) } - m.Gas = 0 + var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowExec @@ -6391,11 +6414,26 @@ func (m *CallData) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Gas |= uint64(b&0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } + if byteLen < 0 { + return ErrInvalidLengthExec + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthExec + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Gas = append(m.Gas[:0], dAtA[iNdEx:postIndex]...) + if m.Gas == nil { + m.Gas = []byte{} + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipExec(dAtA[iNdEx:]) diff --git a/execution/execution_test.go b/execution/execution_test.go index 3fc66a543..3902d9bcf 100644 --- a/execution/execution_test.go +++ b/execution/execution_test.go @@ -11,6 +11,8 @@ import ( "testing" "time" + "github.com/hyperledger/burrow/execution/engine" + "github.com/hyperledger/burrow/acm" "github.com/hyperledger/burrow/acm/acmstate" "github.com/hyperledger/burrow/bcm" @@ -1255,7 +1257,7 @@ func TestOrigin(t *testing.T) { // Set a contract that stores the origin address in storage at loc loc := []byte{3} - err := native.UpdateAccount(exe.stateCache, calleeAddress, func(acc *acm.Account) error { + err := engine.UpdateAccount(exe.stateCache, calleeAddress, func(acc *acm.Account) error { acc.EVMCode = bc.MustSplice(ORIGIN, PUSH1, loc, SSTORE) return nil }) diff --git a/execution/native/call.go b/execution/native/call.go deleted file mode 100644 index ed23ce17a..000000000 --- a/execution/native/call.go +++ /dev/null @@ -1,39 +0,0 @@ -package native - -import ( - "github.com/hyperledger/burrow/execution/engine" - "github.com/hyperledger/burrow/execution/errors" - "github.com/hyperledger/burrow/execution/exec" -) - -// Call provides a standard wrapper for implementing Callable.Call with appropriate error handling and event firing. -func Call(state engine.State, params engine.CallParams, execute func(engine.State, engine.CallParams) ([]byte, error)) ([]byte, error) { - maybe := new(errors.Maybe) - if params.CallType == exec.CallTypeCall || params.CallType == exec.CallTypeCode { - // NOTE: Delegate and Static CallTypes do not transfer the value to the callee. - maybe.PushError(Transfer(state.CallFrame, params.Caller, params.Callee, params.Value)) - } - - output := maybe.Bytes(execute(state, params)) - // fire the post call event (including exception if applicable) and make sure we return the accumulated call error - maybe.PushError(FireCallEvent(state.CallFrame, maybe.Error(), state.EventSink, output, params)) - return output, maybe.Error() -} - -func FireCallEvent(callFrame *engine.CallFrame, callErr error, eventSink exec.EventSink, output []byte, - params engine.CallParams) error { - // fire the post call event (including exception if applicable) - return eventSink.Call(&exec.CallEvent{ - CallType: params.CallType, - CallData: &exec.CallData{ - Caller: params.Caller, - Callee: params.Callee, - Data: params.Input, - Value: params.Value, - Gas: *params.Gas, - }, - Origin: params.Origin, - StackDepth: callFrame.CallStackDepth(), - Return: output, - }, errors.AsException(callErr)) -} diff --git a/execution/native/contract.go b/execution/native/contract.go index cc5779e40..bc91db387 100644 --- a/execution/native/contract.go +++ b/execution/native/contract.go @@ -58,7 +58,7 @@ var _ Native = &Contract{} // Create a new native contract description object by passing a comment, name // and a list of member functions descriptions func NewContract(name string, comment string, logger *logging.Logger, fs ...Function) (*Contract, error) { - address := AddressFromName(name) + address := engine.AddressFromName(name) functionsByID := make(map[abi.FunctionID]*Function, len(fs)) functions := make([]*Function, len(fs)) logger = logger.WithScope("NativeContract") @@ -168,9 +168,3 @@ func (c *Contract) ContractMeta() []*acm.ContractMeta { }, } } - -func AddressFromName(name string) (address crypto.Address) { - hash := crypto.Keccak256([]byte(name)) - copy(address[:], hash[len(hash)-crypto.AddressLength:]) - return -} diff --git a/execution/native/function.go b/execution/native/function.go index c9108323c..f8f9f367f 100644 --- a/execution/native/function.go +++ b/execution/native/function.go @@ -72,12 +72,12 @@ func (f *Function) SetExternals(externals engine.Dispatcher) { } func (f *Function) Call(state engine.State, params engine.CallParams) ([]byte, error) { - return Call(state, params, f.execute) + return engine.Call(state, params, f.execute) } func (f *Function) execute(state engine.State, params engine.CallParams) ([]byte, error) { // check if we have permission to call this function - hasPermission, err := HasPermission(state.CallFrame, params.Caller, f.PermFlag) + hasPermission, err := engine.HasPermission(state.CallFrame, params.Caller, f.PermFlag) if err != nil { return nil, err } diff --git a/execution/native/permissions.go b/execution/native/permissions.go index cfb87d04b..98aa29d69 100644 --- a/execution/native/permissions.go +++ b/execution/native/permissions.go @@ -3,8 +3,9 @@ package native import ( "fmt" + "github.com/hyperledger/burrow/execution/engine" + "github.com/hyperledger/burrow/acm" - "github.com/hyperledger/burrow/acm/acmstate" "github.com/hyperledger/burrow/crypto" "github.com/hyperledger/burrow/permission" ) @@ -85,32 +86,6 @@ var Permissions = New().MustContract("Permissions", }, ) -// CONTRACT: it is the duty of the contract writer to call known permissions -// we do not convey if a permission is not set -// (unlike in state/execution, where we guarantee HasPermission is called -// on known permissions and panics else) -// If the perm is not defined in the acc nor set by default in GlobalPermissions, -// this function returns false. -func HasPermission(st acmstate.Reader, address crypto.Address, perm permission.PermFlag) (bool, error) { - acc, err := st.GetAccount(address) - if err != nil { - return false, err - } - if acc == nil { - return false, fmt.Errorf("account %v does not exist", address) - } - globalPerms, err := acmstate.GlobalAccountPermissions(st) - if err != nil { - return false, err - } - perms := acc.Permissions.Base.Compose(globalPerms.Base) - value, err := perms.Get(perm) - if err != nil { - return false, err - } - return value, nil -} - type hasBaseArgs struct { Account crypto.Address Permission uint64 @@ -125,7 +100,7 @@ func hasBase(ctx Context, args hasBaseArgs) (hasBaseRets, error) { if !permN.IsValid() { return hasBaseRets{}, permission.ErrInvalidPermission(permN) } - hasPermission, err := HasPermission(ctx.State, args.Account, permN) + hasPermission, err := engine.HasPermission(ctx.State, args.Account, permN) if err != nil { return hasBaseRets{}, err } @@ -151,7 +126,7 @@ func setBase(ctx Context, args setBaseArgs) (setBaseRets, error) { if !permFlag.IsValid() { return setBaseRets{}, permission.ErrInvalidPermission(permFlag) } - err := UpdateAccount(ctx.State, args.Account, func(acc *acm.Account) error { + err := engine.UpdateAccount(ctx.State, args.Account, func(acc *acm.Account) error { err := acc.Permissions.Base.Set(permFlag, args.Set) return err }) @@ -178,7 +153,7 @@ func unsetBase(ctx Context, args unsetBaseArgs) (unsetBaseRets, error) { if !permFlag.IsValid() { return unsetBaseRets{}, permission.ErrInvalidPermission(permFlag) } - err := UpdateAccount(ctx.State, args.Account, func(acc *acm.Account) error { + err := engine.UpdateAccount(ctx.State, args.Account, func(acc *acm.Account) error { return acc.Permissions.Base.Unset(permFlag) }) if err != nil { @@ -205,7 +180,7 @@ func setGlobal(ctx Context, args setGlobalArgs) (setGlobalRets, error) { if !permFlag.IsValid() { return setGlobalRets{}, permission.ErrInvalidPermission(permFlag) } - err := UpdateAccount(ctx.State, acm.GlobalPermissionsAddress, func(acc *acm.Account) error { + err := engine.UpdateAccount(ctx.State, acm.GlobalPermissionsAddress, func(acc *acm.Account) error { return acc.Permissions.Base.Set(permFlag, args.Set) }) if err != nil { @@ -227,7 +202,7 @@ type hasRoleRets struct { } func hasRole(ctx Context, args hasRoleArgs) (hasRoleRets, error) { - acc, err := mustAccount(ctx.State, args.Account) + acc, err := engine.MustAccount(ctx.State, args.Account) if err != nil { return hasRoleRets{}, err } @@ -249,7 +224,7 @@ type addRoleRets struct { func addRole(ctx Context, args addRoleArgs) (addRoleRets, error) { ret := addRoleRets{} - err := UpdateAccount(ctx.State, args.Account, func(account *acm.Account) error { + err := engine.UpdateAccount(ctx.State, args.Account, func(account *acm.Account) error { ret.Result = account.Permissions.AddRole(args.Role) return nil }) @@ -273,7 +248,7 @@ type removeRoleRets struct { func removeRole(ctx Context, args removeRoleArgs) (removeRoleRets, error) { ret := removeRoleRets{} - err := UpdateAccount(ctx.State, args.Account, func(account *acm.Account) error { + err := engine.UpdateAccount(ctx.State, args.Account, func(account *acm.Account) error { ret.Result = account.Permissions.RemoveRole(args.Role) return nil }) diff --git a/execution/native/permissions_test.go b/execution/native/permissions_test.go index 269f2884e..ebe38fa96 100644 --- a/execution/native/permissions_test.go +++ b/execution/native/permissions_test.go @@ -5,6 +5,7 @@ package native import ( "encoding/hex" + "math/big" "strconv" "strings" "testing" @@ -79,14 +80,12 @@ func TestSNativeContractDescription_Dispatch(t *testing.T) { function := contract.FunctionByName("setBase") require.NotNil(t, function, "Could not get function: %s") funcID := function.Abi().FunctionID - gas := uint64(1000) - // Should fail since we have no permissions input := bc.MustSplice(funcID[:], grantee.Address, permFlagToWord256(permission.CreateAccount)) params := engine.CallParams{ Caller: caller.Address, Input: input, - Gas: &gas, + Gas: big.NewInt(1000), } _, err := contract.Call(state, params) if !assert.Error(t, err, "Should fail due to lack of permissions") { @@ -95,7 +94,7 @@ func TestSNativeContractDescription_Dispatch(t *testing.T) { assert.Equal(t, errors.Codes.NativeFunction, errors.GetCode(err)) // Grant all permissions and dispatch should success - err = UpdateAccount(state, caller.Address, func(acc *acm.Account) error { + err = engine.UpdateAccount(state, caller.Address, func(acc *acm.Account) error { return acc.Permissions.Base.Set(permission.SetBase, true) }) require.NoError(t, err) @@ -120,7 +119,7 @@ func TestHasPermission(t *testing.T) { require.NoError(t, err) acc := &acm.Account{ - Address: AddressFromName("frog"), + Address: engine.AddressFromName("frog"), Permissions: permission.AccountPermissions{ Base: base, }, @@ -130,7 +129,7 @@ func TestHasPermission(t *testing.T) { // Ensure we are falling through to global permissions on those bits not set flag := permission.Send | permission.Call | permission.Name | permission.HasRole - hasPermission, err := HasPermission(cache, acc.Address, flag) + hasPermission, err := engine.HasPermission(cache, acc.Address, flag) require.NoError(t, err) assert.True(t, hasPermission) } diff --git a/execution/native/precompiles.go b/execution/native/precompiles.go index f9cd27bb6..d9cc7b93d 100644 --- a/execution/native/precompiles.go +++ b/execution/native/precompiles.go @@ -5,11 +5,12 @@ import ( "fmt" "math/big" + "github.com/hyperledger/burrow/execution/engine" + "github.com/btcsuite/btcd/btcec" "github.com/hyperledger/burrow/binary" "github.com/hyperledger/burrow/crypto" - "github.com/hyperledger/burrow/execution/errors" "github.com/hyperledger/burrow/permission" "golang.org/x/crypto/ripemd160" ) @@ -43,11 +44,10 @@ func leftPadAddress(bs ...byte) crypto.Address { // SECP256K1 Recovery func ecrecover(ctx Context) ([]byte, error) { // Deduct gas - gasRequired := GasEcRecover - if *ctx.Gas < gasRequired { - return nil, errors.Codes.InsufficientGas - } else { - *ctx.Gas -= gasRequired + gasRequired := engine.GasEcRecover + var err error = engine.UseGasNegative(ctx.Gas, gasRequired) + if err != nil { + return nil, err } // layout is: @@ -95,11 +95,10 @@ func ecrecover(ctx Context) ([]byte, error) { func sha256(ctx Context) (output []byte, err error) { // Deduct gas - gasRequired := wordsIn(uint64(len(ctx.Input)))*GasSha256Word + GasSha256Base - if *ctx.Gas < gasRequired { - return nil, errors.Codes.InsufficientGas - } else { - *ctx.Gas -= gasRequired + gasRequired := wordsIn(uint64(len(ctx.Input)))*engine.GasSha256Word + engine.GasSha256Base + err = engine.UseGasNegative(ctx.Gas, gasRequired) + if err != nil { + return nil, err } // Hash hasher := cryptoSha256.New() @@ -110,11 +109,10 @@ func sha256(ctx Context) (output []byte, err error) { func ripemd160Func(ctx Context) (output []byte, err error) { // Deduct gas - gasRequired := wordsIn(uint64(len(ctx.Input)))*GasRipemd160Word + GasRipemd160Base - if *ctx.Gas < gasRequired { - return nil, errors.Codes.InsufficientGas - } else { - *ctx.Gas -= gasRequired + gasRequired := wordsIn(uint64(len(ctx.Input)))*engine.GasRipemd160Word + engine.GasRipemd160Base + err = engine.UseGasNegative(ctx.Gas, gasRequired) + if err != nil { + return nil, err } // Hash hasher := ripemd160.New() @@ -125,11 +123,10 @@ func ripemd160Func(ctx Context) (output []byte, err error) { func identity(ctx Context) (output []byte, err error) { // Deduct gas - gasRequired := wordsIn(uint64(len(ctx.Input)))*GasIdentityWord + GasIdentityBase - if *ctx.Gas < gasRequired { - return nil, errors.Codes.InsufficientGas - } else { - *ctx.Gas -= gasRequired + gasRequired := wordsIn(uint64(len(ctx.Input)))*engine.GasIdentityWord + engine.GasIdentityBase + err = engine.UseGasNegative(ctx.Gas, gasRequired) + if err != nil { + return nil, err } // Return identity return ctx.Input, nil @@ -152,14 +149,13 @@ func expMod(ctx Context) (output []byte, err error) { // TODO: implement non-trivial gas schedule for this operation. Probably a parameterised version of the one // described in EIP though that one seems like a bit of a complicated fudge - gasRequired := GasExpModBase + GasExpModWord*(wordsIn(baseLength)*wordsIn(expLength)*wordsIn(modLength)) + gasRequired := engine.GasExpModBase + engine.GasExpModWord*(wordsIn(baseLength)*wordsIn(expLength)*wordsIn(modLength)) - if *ctx.Gas < gasRequired { - return nil, errors.Codes.InsufficientGas + err = engine.UseGasNegative(ctx.Gas, gasRequired) + if err != nil { + return nil, err } - *ctx.Gas -= gasRequired - input, segments, err = cut(input, baseLength, expLength, modLength) if err != nil { return nil, fmt.Errorf("%s: %v", errHeader, err) diff --git a/execution/wasm/contract.go b/execution/wasm/contract.go new file mode 100644 index 000000000..c01d56cbe --- /dev/null +++ b/execution/wasm/contract.go @@ -0,0 +1,280 @@ +package wasm + +import ( + "encoding/binary" + "fmt" + "math/big" + + "github.com/hyperledger/burrow/execution/exec" + + bin "github.com/hyperledger/burrow/binary" + "github.com/hyperledger/burrow/crypto" + "github.com/hyperledger/burrow/execution/engine" + "github.com/hyperledger/burrow/execution/errors" + lifeExec "github.com/perlin-network/life/exec" +) + +type Contract struct { + vm *WVM + code []byte +} + +const Success = 0 +const Error = 1 +const Revert = 2 + +const ValueByteSize = 16 + +func (c *Contract) Call(state engine.State, params engine.CallParams) (output []byte, err error) { + return engine.Call(state, params, c.execute) +} + +func (c *Contract) execute(state engine.State, params engine.CallParams) ([]byte, error) { + const errHeader = "ewasm" + + // Since Life runs the execution for us we push the arguments into the import resolver state + ctx := &context{ + Contract: c, + state: state, + params: params, + code: c.code, + } + // panics in ResolveFunc() will be recovered for us, no need for our own + vm, err := lifeExec.NewVirtualMachine(c.code, c.vm.vmConfig, ctx, nil) + if err != nil { + return nil, errors.Errorf(errors.Codes.InvalidContract, "%s: %v", errHeader, err) + } + + entryID, ok := vm.GetFunctionExport("main") + if !ok { + return nil, errors.Codes.UnresolvedSymbols + } + + _, err = vm.Run(entryID) + if err != nil && errors.GetCode(err) != errors.Codes.None { + return nil, errors.Errorf(errors.Codes.ExecutionAborted, "%s: %v", errHeader, err) + } + + return ctx.output, nil +} + +type context struct { + *Contract + state engine.State + params engine.CallParams + code []byte + output []byte + returnData []byte +} + +var _ lifeExec.ImportResolver = (*context)(nil) + +func (ctx *context) ResolveGlobal(module, field string) int64 { + panic(fmt.Sprintf("global %s module %s not found", field, module)) +} + +func (ctx *context) ResolveFunc(module, field string) lifeExec.FunctionImport { + if module != "ethereum" { + panic(fmt.Sprintf("unknown module %s", module)) + } + + switch field { + case "call": + return func(vm *lifeExec.VirtualMachine) int64 { + gasLimit := big.NewInt(vm.GetCurrentFrame().Locals[0]) + addressPtr := uint32(vm.GetCurrentFrame().Locals[1]) + valuePtr := int(uint32(vm.GetCurrentFrame().Locals[2])) + dataPtr := uint32(vm.GetCurrentFrame().Locals[3]) + dataLen := uint32(vm.GetCurrentFrame().Locals[4]) + + // TODO: avoid panic? Or at least panic with coded out-of-bounds + target := crypto.MustAddressFromBytes(vm.Memory[addressPtr : addressPtr+crypto.AddressLength]) + + // TODO: is this guaranteed to be okay? Should be avoid panic here if out of bounds? + value := bin.BigIntFromLittleEndianBytes(vm.Memory[valuePtr : valuePtr+ValueByteSize]) + + var err error + ctx.returnData, err = engine.CallFromSite(ctx.state, ctx.vm, ctx.params, engine.CallParams{ + CallType: exec.CallTypeCall, + Callee: target, + Input: vm.Memory[dataPtr : dataPtr+dataLen], + Value: *value, + Gas: gasLimit, + }) + + // Refund any remaining gas to be used on subsequent calls + ctx.params.Gas.Add(ctx.params.Gas, gasLimit) + + // TODO[Silas]: we may need to consider trapping and non-trapping errors here in a bit more of a principled way + // (e.g. we may be currently handling things that should abort execution, it might be better to clasify + // all of our coded errors as trapping (fatal abort WASM) or non-trapping (return error to WASM caller) + // I'm not sure this is consistent in EVM either. + if err != nil { + if errors.GetCode(err) == errors.Codes.ExecutionReverted { + return Revert + } + // Spec says return 1 for error, but not sure when to do that (as opposed to abort): + // https://github.com/ewasm/design/blob/master/eth_interface.md#call + panic(err) + return Error + } + return Success + } + + case "getCallDataSize": + return func(vm *lifeExec.VirtualMachine) int64 { + return int64(len(ctx.params.Input)) + } + + case "callDataCopy": + return func(vm *lifeExec.VirtualMachine) int64 { + destPtr := int(uint32(vm.GetCurrentFrame().Locals[0])) + dataOffset := int(uint32(vm.GetCurrentFrame().Locals[1])) + dataLen := int(uint32(vm.GetCurrentFrame().Locals[2])) + + if dataLen > 0 { + copy(vm.Memory[destPtr:], ctx.params.Input[dataOffset:dataOffset+dataLen]) + } + + return Success + } + + case "getReturnDataSize": + return func(vm *lifeExec.VirtualMachine) int64 { + return int64(len(ctx.returnData)) + } + + case "returnDataCopy": + return func(vm *lifeExec.VirtualMachine) int64 { + destPtr := int(uint32(vm.GetCurrentFrame().Locals[0])) + dataOffset := int(uint32(vm.GetCurrentFrame().Locals[1])) + dataLen := int(uint32(vm.GetCurrentFrame().Locals[2])) + + if dataLen > 0 { + copy(vm.Memory[destPtr:], ctx.returnData[dataOffset:dataOffset+dataLen]) + } + + return Success + } + + case "getCodeSize": + return func(vm *lifeExec.VirtualMachine) int64 { + return int64(len(ctx.code)) + } + + case "codeCopy": + return func(vm *lifeExec.VirtualMachine) int64 { + destPtr := int(uint32(vm.GetCurrentFrame().Locals[0])) + dataOffset := int(uint32(vm.GetCurrentFrame().Locals[1])) + dataLen := int(uint32(vm.GetCurrentFrame().Locals[2])) + + if dataLen > 0 { + copy(vm.Memory[destPtr:], ctx.code[dataOffset:dataOffset+dataLen]) + } + + return Success + } + + case "storageStore": + return func(vm *lifeExec.VirtualMachine) int64 { + keyPtr := int(uint32(vm.GetCurrentFrame().Locals[0])) + dataPtr := int(uint32(vm.GetCurrentFrame().Locals[1])) + + key := bin.Word256{} + value := make([]byte, 32) + + copy(key[:], vm.Memory[keyPtr:keyPtr+32]) + copy(value, vm.Memory[dataPtr:dataPtr+32]) + + err := ctx.state.SetStorage(ctx.params.Callee, key, value) + if err != nil { + panic(err) + } + return Success + } + + case "storageLoad": + return func(vm *lifeExec.VirtualMachine) int64 { + + keyPtr := int(uint32(vm.GetCurrentFrame().Locals[0])) + dataPtr := int(uint32(vm.GetCurrentFrame().Locals[1])) + + key := bin.Word256{} + + copy(key[:], vm.Memory[keyPtr:keyPtr+32]) + + val, err := ctx.state.GetStorage(ctx.params.Callee, key) + if err != nil { + panic(err) + } + copy(vm.Memory[dataPtr:], val) + + return Success + } + + case "finish": + return func(vm *lifeExec.VirtualMachine) int64 { + dataPtr := int(uint32(vm.GetCurrentFrame().Locals[0])) + dataLen := int(uint32(vm.GetCurrentFrame().Locals[1])) + + ctx.output = vm.Memory[dataPtr : dataPtr+dataLen] + + panic(errors.Codes.None) + } + + case "revert": + return func(vm *lifeExec.VirtualMachine) int64 { + + dataPtr := int(uint32(vm.GetCurrentFrame().Locals[0])) + dataLen := int(uint32(vm.GetCurrentFrame().Locals[1])) + + ctx.output = vm.Memory[dataPtr : dataPtr+dataLen] + + panic(errors.Codes.ExecutionReverted) + } + + case "getAddress": + return func(vm *lifeExec.VirtualMachine) int64 { + addressPtr := int(uint32(vm.GetCurrentFrame().Locals[0])) + + copy(vm.Memory[addressPtr:], ctx.params.Callee.Bytes()) + + return Success + } + + case "getCallValue": + return func(vm *lifeExec.VirtualMachine) int64 { + valuePtr := int(uint32(vm.GetCurrentFrame().Locals[0])) + + // ewasm value is little endian 128 bit value + copy(vm.Memory[valuePtr:], bin.BigIntToLittleEndianBytes(&ctx.params.Value)) + + return Success + } + + case "getExternalBalance": + return func(vm *lifeExec.VirtualMachine) int64 { + addressPtr := int(uint32(vm.GetCurrentFrame().Locals[0])) + balancePtr := int(uint32(vm.GetCurrentFrame().Locals[1])) + + address := crypto.Address{} + + copy(address[:], vm.Memory[addressPtr:addressPtr+crypto.AddressLength]) + acc, err := ctx.state.GetAccount(address) + if err != nil { + panic(errors.Codes.InvalidAddress) + } + + // ewasm value is little endian 128 bit value + bs := make([]byte, 16) + binary.LittleEndian.PutUint64(bs, acc.Balance) + + copy(vm.Memory[balancePtr:], bs) + + return Success + } + + default: + panic(fmt.Sprintf("unknown function %s", field)) + } +} diff --git a/execution/wasm/wasm.go b/execution/wasm/wasm.go index 2720d6123..eeb2868c1 100644 --- a/execution/wasm/wasm.go +++ b/execution/wasm/wasm.go @@ -1,232 +1,92 @@ package wasm import ( - "encoding/binary" - "fmt" + "github.com/hyperledger/burrow/acm" + "github.com/hyperledger/burrow/execution/exec" + "github.com/hyperledger/burrow/execution/native" + "github.com/hyperledger/burrow/logging" "github.com/hyperledger/burrow/acm/acmstate" - burrow_binary "github.com/hyperledger/burrow/binary" - "github.com/hyperledger/burrow/crypto" "github.com/hyperledger/burrow/execution/engine" "github.com/hyperledger/burrow/execution/errors" - "github.com/perlin-network/life/exec" + lifeExec "github.com/perlin-network/life/exec" ) -type execContext struct { - errors.Maybe - code []byte - output []byte - returnData []byte - params engine.CallParams - state acmstate.ReaderWriter +// Implements ewasm, see https://github.com/ewasm/design +// WASM +var DefaultVMConfig = lifeExec.VMConfig{ + DisableFloatingPoint: true, + MaxMemoryPages: 16, + DefaultMemoryPages: 16, } -// Implements ewasm, see https://github.com/ewasm/design +type WVM struct { + engine.Externals + options Options + vmConfig lifeExec.VMConfig +} + +type Options struct { + Natives *native.Natives + CallStackMaxDepth uint64 + Logger *logging.Logger +} + +func New(options Options) *WVM { + if options.Natives == nil { + options.Natives = native.MustDefaultNatives() + } + if options.Logger == nil { + options.Logger = logging.NewNoopLogger() + } + return &WVM{ + options: options, + vmConfig: DefaultVMConfig, + } +} + +func Default() *WVM { + return New(Options{}) +} // RunWASM creates a WASM VM, and executes the given WASM contract code -func RunWASM(state acmstate.ReaderWriter, params engine.CallParams, wasm []byte) (output []byte, cerr error) { - const errHeader = "ewasm" +func (vm *WVM) Execute(st acmstate.ReaderWriter, blockchain engine.Blockchain, eventSink exec.EventSink, + params engine.CallParams, code []byte) (output []byte, cerr error) { defer func() { if r := recover(); r != nil { cerr = errors.Codes.ExecutionAborted } }() - // WASM - config := exec.VMConfig{ - DisableFloatingPoint: true, - MaxMemoryPages: 16, - DefaultMemoryPages: 16, - } - - execContext := execContext{ - params: params, - code: wasm, - state: state, - } + st = native.NewState(vm.options.Natives, st) - // panics in ResolveFunc() will be recovered for us, no need for our own - vm, err := exec.NewVirtualMachine(wasm, config, &execContext, nil) - if err != nil { - return nil, errors.Errorf(errors.Codes.InvalidContract, "%s: %v", errHeader, err) - } - if execContext.Error() != nil { - return nil, execContext.Error() + state := engine.State{ + CallFrame: engine.NewCallFrame(st).WithMaxCallStackDepth(vm.options.CallStackMaxDepth), + Blockchain: blockchain, + EventSink: eventSink, } - entryID, ok := vm.GetFunctionExport("main") - if !ok { - return nil, errors.Codes.UnresolvedSymbols - } + output, err := vm.Contract(code).Call(state, params) - _, err = vm.Run(entryID) - if err != nil && errors.GetCode(err) != errors.Codes.None { - return nil, errors.Errorf(errors.Codes.ExecutionAborted, "%s: %v", errHeader, err) + if err == nil { + // Only sync back when there was no exception + err = state.CallFrame.Sync() } - - return execContext.output, nil + // Always return output - we may have a reverted exception for which the return is meaningful + return output, err } -func (e *execContext) ResolveFunc(module, field string) exec.FunctionImport { - if module != "ethereum" { - panic(fmt.Sprintf("unknown module %s", module)) - } - - switch field { - case "getCallDataSize": - return func(vm *exec.VirtualMachine) int64 { - return int64(len(e.params.Input)) - } - - case "callDataCopy": - return func(vm *exec.VirtualMachine) int64 { - destPtr := int(uint32(vm.GetCurrentFrame().Locals[0])) - dataOffset := int(uint32(vm.GetCurrentFrame().Locals[1])) - dataLen := int(uint32(vm.GetCurrentFrame().Locals[2])) - - if dataLen > 0 { - copy(vm.Memory[destPtr:], e.params.Input[dataOffset:dataOffset+dataLen]) - } - - return 0 - } - - case "getReturnDataSize": - return func(vm *exec.VirtualMachine) int64 { - return int64(len(e.returnData)) - } - - case "returnDataCopy": - return func(vm *exec.VirtualMachine) int64 { - destPtr := int(uint32(vm.GetCurrentFrame().Locals[0])) - dataOffset := int(uint32(vm.GetCurrentFrame().Locals[1])) - dataLen := int(uint32(vm.GetCurrentFrame().Locals[2])) - - if dataLen > 0 { - copy(vm.Memory[destPtr:], e.returnData[dataOffset:dataOffset+dataLen]) - } - - return 0 - } - - case "getCodeSize": - return func(vm *exec.VirtualMachine) int64 { - return int64(len(e.code)) - } - - case "codeCopy": - return func(vm *exec.VirtualMachine) int64 { - destPtr := int(uint32(vm.GetCurrentFrame().Locals[0])) - dataOffset := int(uint32(vm.GetCurrentFrame().Locals[1])) - dataLen := int(uint32(vm.GetCurrentFrame().Locals[2])) - - if dataLen > 0 { - copy(vm.Memory[destPtr:], e.code[dataOffset:dataOffset+dataLen]) - } - - return 0 - } - - case "storageStore": - return func(vm *exec.VirtualMachine) int64 { - keyPtr := int(uint32(vm.GetCurrentFrame().Locals[0])) - dataPtr := int(uint32(vm.GetCurrentFrame().Locals[1])) - - key := burrow_binary.Word256{} - - copy(key[:], vm.Memory[keyPtr:keyPtr+32]) - - e.Void(e.state.SetStorage(e.params.Callee, key, vm.Memory[dataPtr:dataPtr+32])) - return 0 - } - - case "storageLoad": - return func(vm *exec.VirtualMachine) int64 { - - keyPtr := int(uint32(vm.GetCurrentFrame().Locals[0])) - dataPtr := int(uint32(vm.GetCurrentFrame().Locals[1])) - - key := burrow_binary.Word256{} - - copy(key[:], vm.Memory[keyPtr:keyPtr+32]) - - val := e.Bytes(e.state.GetStorage(e.params.Callee, key)) - copy(vm.Memory[dataPtr:], val) - - return 0 - } - - case "finish": - return func(vm *exec.VirtualMachine) int64 { - dataPtr := int(uint32(vm.GetCurrentFrame().Locals[0])) - dataLen := int(uint32(vm.GetCurrentFrame().Locals[1])) - - e.output = vm.Memory[dataPtr : dataPtr+dataLen] - - panic(errors.Codes.None) - } - - case "revert": - return func(vm *exec.VirtualMachine) int64 { - - dataPtr := int(uint32(vm.GetCurrentFrame().Locals[0])) - dataLen := int(uint32(vm.GetCurrentFrame().Locals[1])) - - e.output = vm.Memory[dataPtr : dataPtr+dataLen] - - panic(errors.Codes.ExecutionReverted) - } - - case "getAddress": - return func(vm *exec.VirtualMachine) int64 { - addressPtr := int(uint32(vm.GetCurrentFrame().Locals[0])) - - copy(vm.Memory[addressPtr:], e.params.Callee.Bytes()) - - return 0 - } - - case "getCallValue": - return func(vm *exec.VirtualMachine) int64 { - - valuePtr := int(uint32(vm.GetCurrentFrame().Locals[0])) - - // ewasm value is little endian 128 bit value - bs := make([]byte, 16) - binary.LittleEndian.PutUint64(bs, e.params.Value) - - copy(vm.Memory[valuePtr:], bs) - - return 0 - } - - case "getExternalBalance": - return func(vm *exec.VirtualMachine) int64 { - addressPtr := int(uint32(vm.GetCurrentFrame().Locals[0])) - balancePtr := int(uint32(vm.GetCurrentFrame().Locals[1])) - - address := crypto.Address{} - - copy(address[:], vm.Memory[addressPtr:addressPtr+crypto.AddressLength]) - acc, err := e.state.GetAccount(address) - if err != nil { - panic(errors.Codes.InvalidAddress) - } - - // ewasm value is little endian 128 bit value - bs := make([]byte, 16) - binary.LittleEndian.PutUint64(bs, acc.Balance) - - copy(vm.Memory[balancePtr:], bs) - - return 0 - } - - default: - panic(fmt.Sprintf("unknown function %s", field)) +func (vm *WVM) Dispatch(acc *acm.Account) engine.Callable { + callable := vm.Externals.Dispatch(acc) + if callable != nil { + return callable } + return vm.Contract(acc.WASMCode) } -func (e *execContext) ResolveGlobal(module, field string) int64 { - panic(fmt.Sprintf("global %s module %s not found", field, module)) +func (vm *WVM) Contract(code []byte) *Contract { + return &Contract{ + vm: vm, + code: code, + } } diff --git a/execution/wasm/wasm_test.go b/execution/wasm/wasm_test.go index ff9ac1744..e9f59494a 100644 --- a/execution/wasm/wasm_test.go +++ b/execution/wasm/wasm_test.go @@ -2,8 +2,11 @@ package wasm import ( "fmt" + "math/big" "testing" + "github.com/hyperledger/burrow/execution/exec" + "github.com/hyperledger/burrow/acm/acmstate" "github.com/hyperledger/burrow/binary" "github.com/hyperledger/burrow/execution/engine" @@ -16,19 +19,21 @@ import ( func TestStaticCallWithValue(t *testing.T) { cache := acmstate.NewMemoryState() - gas := uint64(0) - params := engine.CallParams{ Origin: crypto.ZeroAddress, Caller: crypto.ZeroAddress, Callee: crypto.ZeroAddress, Input: []byte{}, - Value: 0, - Gas: &gas, + Value: *big.NewInt(0), + Gas: big.NewInt(1000), } + vm := Default() + blockchain := new(engine.TestBlockchain) + eventSink := exec.NewNoopEventSink() + // run constructor - runtime, cerr := RunWASM(cache, params, Bytecode_storage_test) + runtime, cerr := vm.Execute(cache, blockchain, eventSink, params, Bytecode_storage_test) require.NoError(t, cerr) // run getFooPlus2 @@ -38,7 +43,7 @@ func TestStaticCallWithValue(t *testing.T) { params.Input = calldata - returndata, cerr := RunWASM(cache, params, runtime) + returndata, cerr := vm.Execute(cache, blockchain, eventSink, params, runtime) require.NoError(t, cerr) data := abi.GetPackingTypes(spec.Functions["getFooPlus2"].Outputs) @@ -55,7 +60,7 @@ func TestStaticCallWithValue(t *testing.T) { params.Input = calldata - returndata, cerr = RunWASM(cache, params, runtime) + returndata, cerr = vm.Execute(cache, blockchain, eventSink, params, runtime) require.NoError(t, cerr) require.Equal(t, returndata, []byte{}) @@ -66,7 +71,7 @@ func TestStaticCallWithValue(t *testing.T) { params.Input = calldata - returndata, cerr = RunWASM(cache, params, runtime) + returndata, cerr = vm.Execute(cache, blockchain, eventSink, params, runtime) require.NoError(t, cerr) spec.Unpack(returndata, "getFooPlus2", data...) diff --git a/js/proto/exec_pb.d.ts b/js/proto/exec_pb.d.ts index 2bd02ece7..02ad26ba7 100644 --- a/js/proto/exec_pb.d.ts +++ b/js/proto/exec_pb.d.ts @@ -740,11 +740,15 @@ export class CallData extends jspb.Message { getData_asB64(): string; setData(value: Uint8Array | string): CallData; - getValue(): number; - setValue(value: number): CallData; + getValue(): Uint8Array | string; + getValue_asU8(): Uint8Array; + getValue_asB64(): string; + setValue(value: Uint8Array | string): CallData; - getGas(): number; - setGas(value: number): CallData; + getGas(): Uint8Array | string; + getGas_asU8(): Uint8Array; + getGas_asB64(): string; + setGas(value: Uint8Array | string): CallData; serializeBinary(): Uint8Array; @@ -762,7 +766,7 @@ export namespace CallData { caller: Uint8Array | string, callee: Uint8Array | string, data: Uint8Array | string, - value: number, - gas: number, + value: Uint8Array | string, + gas: Uint8Array | string, } } diff --git a/js/proto/exec_pb.js b/js/proto/exec_pb.js index a49aca83c..9056d28f2 100644 --- a/js/proto/exec_pb.js +++ b/js/proto/exec_pb.js @@ -5427,8 +5427,8 @@ proto.exec.CallData.toObject = function(includeInstance, msg) { caller: msg.getCaller_asB64(), callee: msg.getCallee_asB64(), data: msg.getData_asB64(), - value: jspb.Message.getFieldWithDefault(msg, 4, 0), - gas: jspb.Message.getFieldWithDefault(msg, 5, 0) + value: msg.getValue_asB64(), + gas: msg.getGas_asB64() }; if (includeInstance) { @@ -5478,11 +5478,11 @@ proto.exec.CallData.deserializeBinaryFromReader = function(msg, reader) { msg.setData(value); break; case 4: - var value = /** @type {number} */ (reader.readUint64()); + var value = /** @type {!Uint8Array} */ (reader.readBytes()); msg.setValue(value); break; case 5: - var value = /** @type {number} */ (reader.readUint64()); + var value = /** @type {!Uint8Array} */ (reader.readBytes()); msg.setGas(value); break; default: @@ -5535,16 +5535,16 @@ proto.exec.CallData.serializeBinaryToWriter = function(message, writer) { f ); } - f = message.getValue(); - if (f !== 0) { - writer.writeUint64( + f = message.getValue_asU8(); + if (f.length > 0) { + writer.writeBytes( 4, f ); } - f = message.getGas(); - if (f !== 0) { - writer.writeUint64( + f = message.getGas_asU8(); + if (f.length > 0) { + writer.writeBytes( 5, f ); @@ -5679,38 +5679,86 @@ proto.exec.CallData.prototype.setData = function(value) { /** - * optional uint64 Value = 4; - * @return {number} + * optional bytes Value = 4; + * @return {!(string|Uint8Array)} */ proto.exec.CallData.prototype.getValue = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 4, 0)); + return /** @type {!(string|Uint8Array)} */ (jspb.Message.getFieldWithDefault(this, 4, "")); }; /** - * @param {number} value + * optional bytes Value = 4; + * This is a type-conversion wrapper around `getValue()` + * @return {string} + */ +proto.exec.CallData.prototype.getValue_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getValue())); +}; + + +/** + * optional bytes Value = 4; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getValue()` + * @return {!Uint8Array} + */ +proto.exec.CallData.prototype.getValue_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getValue())); +}; + + +/** + * @param {!(string|Uint8Array)} value * @return {!proto.exec.CallData} returns this */ proto.exec.CallData.prototype.setValue = function(value) { - return jspb.Message.setProto3IntField(this, 4, value); + return jspb.Message.setProto3BytesField(this, 4, value); }; /** - * optional uint64 Gas = 5; - * @return {number} + * optional bytes Gas = 5; + * @return {!(string|Uint8Array)} */ proto.exec.CallData.prototype.getGas = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 5, 0)); + return /** @type {!(string|Uint8Array)} */ (jspb.Message.getFieldWithDefault(this, 5, "")); }; /** - * @param {number} value + * optional bytes Gas = 5; + * This is a type-conversion wrapper around `getGas()` + * @return {string} + */ +proto.exec.CallData.prototype.getGas_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getGas())); +}; + + +/** + * optional bytes Gas = 5; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getGas()` + * @return {!Uint8Array} + */ +proto.exec.CallData.prototype.getGas_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getGas())); +}; + + +/** + * @param {!(string|Uint8Array)} value * @return {!proto.exec.CallData} returns this */ proto.exec.CallData.prototype.setGas = function(value) { - return jspb.Message.setProto3IntField(this, 5, value); + return jspb.Message.setProto3BytesField(this, 5, value); }; diff --git a/keys/keys_grpc.pb.go b/keys/keys_grpc.pb.go index 00f2d1450..5b50d28d2 100644 --- a/keys/keys_grpc.pb.go +++ b/keys/keys_grpc.pb.go @@ -12,7 +12,6 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. const _ = grpc.SupportPackageIsVersion7 // KeysClient is the client API for Keys service. @@ -204,7 +203,7 @@ type UnsafeKeysServer interface { } func RegisterKeysServer(s grpc.ServiceRegistrar, srv KeysServer) { - s.RegisterService(&Keys_ServiceDesc, srv) + s.RegisterService(&_Keys_serviceDesc, srv) } func _Keys_GenerateKey_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { @@ -405,10 +404,7 @@ func _Keys_AddName_Handler(srv interface{}, ctx context.Context, dec func(interf return interceptor(ctx, in, info, handler) } -// Keys_ServiceDesc is the grpc.ServiceDesc for Keys service. -// It's only intended for direct use with grpc.RegisterService, -// and not to be introspected or modified (even as a copy) -var Keys_ServiceDesc = grpc.ServiceDesc{ +var _Keys_serviceDesc = grpc.ServiceDesc{ ServiceName: "keys.Keys", HandlerType: (*KeysServer)(nil), Methods: []grpc.MethodDesc{ diff --git a/protobuf/exec.proto b/protobuf/exec.proto index 6e16b2a24..fec8be865 100644 --- a/protobuf/exec.proto +++ b/protobuf/exec.proto @@ -192,6 +192,7 @@ message CallData { bytes Caller = 1 [(gogoproto.customtype) = "github.com/hyperledger/burrow/crypto.Address", (gogoproto.nullable) = false]; bytes Callee = 2 [(gogoproto.customtype) = "github.com/hyperledger/burrow/crypto.Address", (gogoproto.nullable) = false]; bytes Data = 3 [(gogoproto.customtype) = "github.com/hyperledger/burrow/binary.HexBytes", (gogoproto.nullable) = false]; - uint64 Value = 4; - uint64 Gas = 5; + // Bytes of a big integer value + bytes Value = 4; + bytes Gas = 5; } diff --git a/rpc/rpcdump/rpcdump_grpc.pb.go b/rpc/rpcdump/rpcdump_grpc.pb.go index a764fc6ea..a64c467cd 100644 --- a/rpc/rpcdump/rpcdump_grpc.pb.go +++ b/rpc/rpcdump/rpcdump_grpc.pb.go @@ -13,7 +13,6 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. const _ = grpc.SupportPackageIsVersion7 // DumpClient is the client API for Dump service. @@ -32,7 +31,7 @@ func NewDumpClient(cc grpc.ClientConnInterface) DumpClient { } func (c *dumpClient) GetDump(ctx context.Context, in *GetDumpParam, opts ...grpc.CallOption) (Dump_GetDumpClient, error) { - stream, err := c.cc.NewStream(ctx, &Dump_ServiceDesc.Streams[0], "/rpcdump.Dump/GetDump", opts...) + stream, err := c.cc.NewStream(ctx, &_Dump_serviceDesc.Streams[0], "/rpcdump.Dump/GetDump", opts...) if err != nil { return nil, err } @@ -88,7 +87,7 @@ type UnsafeDumpServer interface { } func RegisterDumpServer(s grpc.ServiceRegistrar, srv DumpServer) { - s.RegisterService(&Dump_ServiceDesc, srv) + s.RegisterService(&_Dump_serviceDesc, srv) } func _Dump_GetDump_Handler(srv interface{}, stream grpc.ServerStream) error { @@ -112,10 +111,7 @@ func (x *dumpGetDumpServer) Send(m *dump.Dump) error { return x.ServerStream.SendMsg(m) } -// Dump_ServiceDesc is the grpc.ServiceDesc for Dump service. -// It's only intended for direct use with grpc.RegisterService, -// and not to be introspected or modified (even as a copy) -var Dump_ServiceDesc = grpc.ServiceDesc{ +var _Dump_serviceDesc = grpc.ServiceDesc{ ServiceName: "rpcdump.Dump", HandlerType: (*DumpServer)(nil), Methods: []grpc.MethodDesc{}, diff --git a/rpc/rpcevents/rpcevents_grpc.pb.go b/rpc/rpcevents/rpcevents_grpc.pb.go index 2be3c15e7..3d7f33649 100644 --- a/rpc/rpcevents/rpcevents_grpc.pb.go +++ b/rpc/rpcevents/rpcevents_grpc.pb.go @@ -13,7 +13,6 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. const _ = grpc.SupportPackageIsVersion7 // ExecutionEventsClient is the client API for ExecutionEvents service. @@ -38,7 +37,7 @@ func NewExecutionEventsClient(cc grpc.ClientConnInterface) ExecutionEventsClient } func (c *executionEventsClient) Stream(ctx context.Context, in *BlocksRequest, opts ...grpc.CallOption) (ExecutionEvents_StreamClient, error) { - stream, err := c.cc.NewStream(ctx, &ExecutionEvents_ServiceDesc.Streams[0], "/rpcevents.ExecutionEvents/Stream", opts...) + stream, err := c.cc.NewStream(ctx, &_ExecutionEvents_serviceDesc.Streams[0], "/rpcevents.ExecutionEvents/Stream", opts...) if err != nil { return nil, err } @@ -79,7 +78,7 @@ func (c *executionEventsClient) Tx(ctx context.Context, in *TxRequest, opts ...g } func (c *executionEventsClient) Events(ctx context.Context, in *BlocksRequest, opts ...grpc.CallOption) (ExecutionEvents_EventsClient, error) { - stream, err := c.cc.NewStream(ctx, &ExecutionEvents_ServiceDesc.Streams[1], "/rpcevents.ExecutionEvents/Events", opts...) + stream, err := c.cc.NewStream(ctx, &_ExecutionEvents_serviceDesc.Streams[1], "/rpcevents.ExecutionEvents/Events", opts...) if err != nil { return nil, err } @@ -147,7 +146,7 @@ type UnsafeExecutionEventsServer interface { } func RegisterExecutionEventsServer(s grpc.ServiceRegistrar, srv ExecutionEventsServer) { - s.RegisterService(&ExecutionEvents_ServiceDesc, srv) + s.RegisterService(&_ExecutionEvents_serviceDesc, srv) } func _ExecutionEvents_Stream_Handler(srv interface{}, stream grpc.ServerStream) error { @@ -210,10 +209,7 @@ func (x *executionEventsEventsServer) Send(m *EventsResponse) error { return x.ServerStream.SendMsg(m) } -// ExecutionEvents_ServiceDesc is the grpc.ServiceDesc for ExecutionEvents service. -// It's only intended for direct use with grpc.RegisterService, -// and not to be introspected or modified (even as a copy) -var ExecutionEvents_ServiceDesc = grpc.ServiceDesc{ +var _ExecutionEvents_serviceDesc = grpc.ServiceDesc{ ServiceName: "rpcevents.ExecutionEvents", HandlerType: (*ExecutionEventsServer)(nil), Methods: []grpc.MethodDesc{ diff --git a/rpc/rpcquery/rpcquery_grpc.pb.go b/rpc/rpcquery/rpcquery_grpc.pb.go index 5058f27be..76df0c644 100644 --- a/rpc/rpcquery/rpcquery_grpc.pb.go +++ b/rpc/rpcquery/rpcquery_grpc.pb.go @@ -17,7 +17,6 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. const _ = grpc.SupportPackageIsVersion7 // QueryClient is the client API for Query service. @@ -86,7 +85,7 @@ func (c *queryClient) GetStorage(ctx context.Context, in *GetStorageParam, opts } func (c *queryClient) ListAccounts(ctx context.Context, in *ListAccountsParam, opts ...grpc.CallOption) (Query_ListAccountsClient, error) { - stream, err := c.cc.NewStream(ctx, &Query_ServiceDesc.Streams[0], "/rpcquery.Query/ListAccounts", opts...) + stream, err := c.cc.NewStream(ctx, &_Query_serviceDesc.Streams[0], "/rpcquery.Query/ListAccounts", opts...) if err != nil { return nil, err } @@ -127,7 +126,7 @@ func (c *queryClient) GetName(ctx context.Context, in *GetNameParam, opts ...grp } func (c *queryClient) ListNames(ctx context.Context, in *ListNamesParam, opts ...grpc.CallOption) (Query_ListNamesClient, error) { - stream, err := c.cc.NewStream(ctx, &Query_ServiceDesc.Streams[1], "/rpcquery.Query/ListNames", opts...) + stream, err := c.cc.NewStream(ctx, &_Query_serviceDesc.Streams[1], "/rpcquery.Query/ListNames", opts...) if err != nil { return nil, err } @@ -195,7 +194,7 @@ func (c *queryClient) GetProposal(ctx context.Context, in *GetProposalParam, opt } func (c *queryClient) ListProposals(ctx context.Context, in *ListProposalsParam, opts ...grpc.CallOption) (Query_ListProposalsClient, error) { - stream, err := c.cc.NewStream(ctx, &Query_ServiceDesc.Streams[2], "/rpcquery.Query/ListProposals", opts...) + stream, err := c.cc.NewStream(ctx, &_Query_serviceDesc.Streams[2], "/rpcquery.Query/ListProposals", opts...) if err != nil { return nil, err } @@ -322,7 +321,7 @@ type UnsafeQueryServer interface { } func RegisterQueryServer(s grpc.ServiceRegistrar, srv QueryServer) { - s.RegisterService(&Query_ServiceDesc, srv) + s.RegisterService(&_Query_serviceDesc, srv) } func _Query_Status_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { @@ -586,10 +585,7 @@ func _Query_GetBlockHeader_Handler(srv interface{}, ctx context.Context, dec fun return interceptor(ctx, in, info, handler) } -// Query_ServiceDesc is the grpc.ServiceDesc for Query service. -// It's only intended for direct use with grpc.RegisterService, -// and not to be introspected or modified (even as a copy) -var Query_ServiceDesc = grpc.ServiceDesc{ +var _Query_serviceDesc = grpc.ServiceDesc{ ServiceName: "rpcquery.Query", HandlerType: (*QueryServer)(nil), Methods: []grpc.MethodDesc{ diff --git a/rpc/rpctransact/rpctransact_grpc.pb.go b/rpc/rpctransact/rpctransact_grpc.pb.go index 124ee37c0..9a70d6fb1 100644 --- a/rpc/rpctransact/rpctransact_grpc.pb.go +++ b/rpc/rpctransact/rpctransact_grpc.pb.go @@ -15,7 +15,6 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. const _ = grpc.SupportPackageIsVersion7 // TransactClient is the client API for Transact service. @@ -249,7 +248,7 @@ type UnsafeTransactServer interface { } func RegisterTransactServer(s grpc.ServiceRegistrar, srv TransactServer) { - s.RegisterService(&Transact_ServiceDesc, srv) + s.RegisterService(&_Transact_serviceDesc, srv) } func _Transact_BroadcastTxSync_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { @@ -468,10 +467,7 @@ func _Transact_NameTxAsync_Handler(srv interface{}, ctx context.Context, dec fun return interceptor(ctx, in, info, handler) } -// Transact_ServiceDesc is the grpc.ServiceDesc for Transact service. -// It's only intended for direct use with grpc.RegisterService, -// and not to be introspected or modified (even as a copy) -var Transact_ServiceDesc = grpc.ServiceDesc{ +var _Transact_serviceDesc = grpc.ServiceDesc{ ServiceName: "rpctransact.Transact", HandlerType: (*TransactServer)(nil), Methods: []grpc.MethodDesc{