github.com/hyperledger/burrow@v0.34.5-0.20220512172541-77f09336001d/execution/wasm/contract.go (about)

     1  package wasm
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"fmt"
     7  	"math/big"
     8  
     9  	"github.com/go-interpreter/wagon/wasm/leb128"
    10  	lifeExec "github.com/perlin-network/life/exec"
    11  	hex "github.com/tmthrgd/go-hex"
    12  
    13  	bin "github.com/hyperledger/burrow/binary"
    14  	"github.com/hyperledger/burrow/crypto"
    15  	"github.com/hyperledger/burrow/execution/engine"
    16  	"github.com/hyperledger/burrow/execution/errors"
    17  	"github.com/hyperledger/burrow/execution/evm"
    18  	"github.com/hyperledger/burrow/execution/exec"
    19  	"github.com/hyperledger/burrow/permission"
    20  	"github.com/hyperledger/burrow/txs"
    21  )
    22  
    23  type Contract struct {
    24  	vm   *WVM
    25  	code []byte
    26  }
    27  
    28  const Success = 0
    29  const Error = 1
    30  const Revert = 2
    31  
    32  const ValueByteSize = 16
    33  
    34  func (c *Contract) Call(state engine.State, params engine.CallParams) (output []byte, err error) {
    35  	return engine.Call(state, params, c.execute)
    36  }
    37  
    38  func (c *Contract) execute(state engine.State, params engine.CallParams) ([]byte, error) {
    39  	const errHeader = "ewasm"
    40  
    41  	// Since Life runs the execution for us we push the arguments into the import resolver state
    42  	ctx := &context{
    43  		Contract: c,
    44  		state:    state,
    45  		params:   params,
    46  		code:     c.code,
    47  	}
    48  
    49  	// panics in ResolveFunc() will be recovered for us, no need for our own
    50  	vm, err := lifeExec.NewVirtualMachine(c.code[0:int(wasmSize(c.code))], c.vm.vmConfig, ctx, nil)
    51  	if err != nil {
    52  		return nil, errors.Errorf(errors.Codes.InvalidContract, "%s: motherfucker %v", errHeader, err)
    53  	}
    54  
    55  	entryID, ok := vm.GetFunctionExport("main")
    56  	if !ok {
    57  		return nil, errors.Codes.UnresolvedSymbols
    58  	}
    59  
    60  	_, err = vm.Run(entryID)
    61  	if err != nil && errors.GetCode(err) == errors.Codes.ExecutionReverted {
    62  		return nil, err
    63  	}
    64  
    65  	if err != nil && errors.GetCode(err) != errors.Codes.None {
    66  		return nil, errors.Errorf(errors.Codes.ExecutionAborted, "%s: %v", errHeader, err)
    67  	}
    68  
    69  	return ctx.output, nil
    70  }
    71  
    72  type context struct {
    73  	*Contract
    74  	state      engine.State
    75  	params     engine.CallParams
    76  	code       []byte
    77  	output     []byte
    78  	returnData []byte
    79  	sequence   uint64
    80  }
    81  
    82  var _ lifeExec.ImportResolver = (*context)(nil)
    83  
    84  func (ctx *context) ResolveGlobal(module, field string) int64 {
    85  	panic(fmt.Sprintf("global %s module %s not found", field, module))
    86  }
    87  
    88  func (ctx *context) ResolveFunc(module, field string) lifeExec.FunctionImport {
    89  	if module == "debug" {
    90  		// See https://github.com/ewasm/hera#interfaces
    91  		switch field {
    92  		case "print32":
    93  			return func(vm *lifeExec.VirtualMachine) int64 {
    94  				n := int32(vm.GetCurrentFrame().Locals[0])
    95  
    96  				s := fmt.Sprintf("%d", n)
    97  
    98  				err := ctx.state.EventSink.Print(&exec.PrintEvent{
    99  					Address: ctx.params.Callee,
   100  					Data:    []byte(s),
   101  				})
   102  
   103  				if err != nil {
   104  					panic(fmt.Sprintf(" => print32 failed: %v", err))
   105  				}
   106  
   107  				return Success
   108  			}
   109  
   110  		case "print64":
   111  			return func(vm *lifeExec.VirtualMachine) int64 {
   112  				n := int64(vm.GetCurrentFrame().Locals[0])
   113  
   114  				s := fmt.Sprintf("%d", n)
   115  
   116  				err := ctx.state.EventSink.Print(&exec.PrintEvent{
   117  					Address: ctx.params.Callee,
   118  					Data:    []byte(s),
   119  				})
   120  
   121  				if err != nil {
   122  					panic(fmt.Sprintf(" => print32 failed: %v", err))
   123  				}
   124  
   125  				return Success
   126  			}
   127  
   128  		case "printMem":
   129  			return func(vm *lifeExec.VirtualMachine) int64 {
   130  				dataPtr := int(uint32(vm.GetCurrentFrame().Locals[0]))
   131  				dataLen := int(uint32(vm.GetCurrentFrame().Locals[1]))
   132  
   133  				s := vm.Memory[dataPtr : dataPtr+dataLen]
   134  
   135  				err := ctx.state.EventSink.Print(&exec.PrintEvent{
   136  					Address: ctx.params.Callee,
   137  					Data:    s,
   138  				})
   139  
   140  				if err != nil {
   141  					panic(fmt.Sprintf(" => printMem failed: %v", err))
   142  				}
   143  
   144  				return Success
   145  			}
   146  
   147  		case "printMemHex":
   148  			return func(vm *lifeExec.VirtualMachine) int64 {
   149  				dataPtr := int(uint32(vm.GetCurrentFrame().Locals[0]))
   150  				dataLen := int(uint32(vm.GetCurrentFrame().Locals[1]))
   151  
   152  				s := hex.EncodeToString(vm.Memory[dataPtr : dataPtr+dataLen])
   153  
   154  				err := ctx.state.EventSink.Print(&exec.PrintEvent{
   155  					Address: ctx.params.Callee,
   156  					Data:    []byte(s),
   157  				})
   158  
   159  				if err != nil {
   160  					panic(fmt.Sprintf(" => printMemHex failed: %v", err))
   161  				}
   162  
   163  				return Success
   164  			}
   165  
   166  		case "printStorage":
   167  			return func(vm *lifeExec.VirtualMachine) int64 {
   168  				keyPtr := int(uint32(vm.GetCurrentFrame().Locals[0]))
   169  
   170  				key := bin.Word256{}
   171  
   172  				copy(key[:], vm.Memory[keyPtr:keyPtr+32])
   173  
   174  				val, err := ctx.state.GetStorage(ctx.params.Callee, key)
   175  				if err != nil {
   176  					panic(err)
   177  				}
   178  
   179  				err = ctx.state.EventSink.Print(&exec.PrintEvent{
   180  					Address: ctx.params.Callee,
   181  					Data:    val,
   182  				})
   183  
   184  				if err != nil {
   185  					panic(fmt.Sprintf(" => printStorage failed: %v", err))
   186  				}
   187  
   188  				return Success
   189  			}
   190  
   191  		case "printStorageHex":
   192  			return func(vm *lifeExec.VirtualMachine) int64 {
   193  				keyPtr := int(uint32(vm.GetCurrentFrame().Locals[0]))
   194  
   195  				key := bin.Word256{}
   196  
   197  				copy(key[:], vm.Memory[keyPtr:keyPtr+32])
   198  
   199  				val, err := ctx.state.GetStorage(ctx.params.Callee, key)
   200  				if err != nil {
   201  					panic(err)
   202  				}
   203  
   204  				s := hex.EncodeToString(val)
   205  
   206  				err = ctx.state.EventSink.Print(&exec.PrintEvent{
   207  					Address: ctx.params.Callee,
   208  					Data:    []byte(s),
   209  				})
   210  
   211  				if err != nil {
   212  					panic(fmt.Sprintf(" => printStorage failed: %v", err))
   213  				}
   214  
   215  				return Success
   216  			}
   217  
   218  		default:
   219  			panic(fmt.Sprintf("function %s unknown for debug module", field))
   220  		}
   221  	}
   222  
   223  	if module != "ethereum" {
   224  		panic(fmt.Sprintf("unknown module %s", module))
   225  	}
   226  
   227  	switch field {
   228  	case "create":
   229  		return func(vm *lifeExec.VirtualMachine) int64 {
   230  			valuePtr := int(uint32(vm.GetCurrentFrame().Locals[0]))
   231  			dataPtr := uint32(vm.GetCurrentFrame().Locals[1])
   232  			dataLen := uint32(vm.GetCurrentFrame().Locals[2])
   233  			resultPtr := uint32(vm.GetCurrentFrame().Locals[3])
   234  
   235  			// TODO: is this guaranteed to be okay? Should be avoid panic here if out of bounds?
   236  			value := bin.BigIntFromLittleEndianBytes(vm.Memory[valuePtr : valuePtr+ValueByteSize])
   237  
   238  			var data []byte
   239  			copy(data, vm.Memory[dataPtr:dataPtr+dataLen])
   240  
   241  			ctx.sequence++
   242  			nonce := make([]byte, txs.HashLength+8)
   243  			copy(nonce, ctx.vm.options.Nonce)
   244  			binary.BigEndian.PutUint64(nonce[txs.HashLength:], ctx.sequence)
   245  			newAccountAddress := crypto.NewContractAddress(ctx.params.Callee, nonce)
   246  
   247  			err := engine.EnsurePermission(ctx.state.CallFrame, ctx.params.Callee, permission.CreateContract)
   248  			if err != nil {
   249  				return Error
   250  			}
   251  
   252  			err = ctx.state.CallFrame.CreateAccount(ctx.params.Caller, newAccountAddress)
   253  			if err != nil {
   254  				return Error
   255  			}
   256  
   257  			res, err := ctx.vm.Contract(vm.Memory[dataPtr:dataPtr+dataLen]).Call(ctx.state, engine.CallParams{
   258  				Caller: ctx.params.Caller,
   259  				Callee: newAccountAddress,
   260  				Input:  nil,
   261  				Value:  *value,
   262  				Gas:    ctx.params.Gas,
   263  			})
   264  
   265  			if err != nil {
   266  				if errors.GetCode(err) == errors.Codes.ExecutionReverted {
   267  					return Revert
   268  				}
   269  				panic(err)
   270  			}
   271  			err = engine.InitWASMCode(ctx.state, newAccountAddress, res)
   272  			if err != nil {
   273  				if errors.GetCode(err) == errors.Codes.ExecutionReverted {
   274  					return Revert
   275  				}
   276  				panic(err)
   277  			}
   278  
   279  			copy(vm.Memory[resultPtr:], newAccountAddress.Bytes())
   280  
   281  			return Success
   282  		}
   283  
   284  	case "getBlockDifficulty":
   285  		return func(vm *lifeExec.VirtualMachine) int64 {
   286  			resultPtr := int(vm.GetCurrentFrame().Locals[0])
   287  
   288  			// set it to 1
   289  			copy(vm.Memory[resultPtr:resultPtr+32], bin.RightPadBytes([]byte{1}, 32))
   290  			return Success
   291  		}
   292  
   293  	case "getTxGasPrice":
   294  		return func(vm *lifeExec.VirtualMachine) int64 {
   295  			resultPtr := int(vm.GetCurrentFrame().Locals[0])
   296  
   297  			// set it to 1
   298  			copy(vm.Memory[resultPtr:resultPtr+16], bin.RightPadBytes([]byte{1}, 16))
   299  			return Success
   300  		}
   301  
   302  	case "selfDestruct":
   303  		return func(vm *lifeExec.VirtualMachine) int64 {
   304  			receiverPtr := int(vm.GetCurrentFrame().Locals[0])
   305  
   306  			var receiver crypto.Address
   307  			copy(receiver[:], vm.Memory[receiverPtr:receiverPtr+crypto.AddressLength])
   308  
   309  			receiverAcc, err := ctx.state.GetAccount(receiver)
   310  			if err != nil {
   311  				panic(err)
   312  			}
   313  			if receiverAcc == nil {
   314  				err := ctx.state.CallFrame.CreateAccount(ctx.params.Callee, receiver)
   315  				if err != nil {
   316  					panic(err)
   317  				}
   318  			}
   319  			acc, err := ctx.state.GetAccount(ctx.params.Callee)
   320  			if err != nil {
   321  				panic(err)
   322  			}
   323  			balance := acc.Balance
   324  			err = acc.AddToBalance(balance)
   325  			if err != nil {
   326  				panic(err)
   327  			}
   328  
   329  			err = ctx.state.CallFrame.UpdateAccount(acc)
   330  			if err != nil {
   331  				panic(err)
   332  			}
   333  			err = ctx.state.CallFrame.RemoveAccount(ctx.params.Callee)
   334  			if err != nil {
   335  				panic(err)
   336  			}
   337  			panic(errors.Codes.None)
   338  		}
   339  
   340  	case "call", "callCode", "callDelegate", "callStatic":
   341  		return func(vm *lifeExec.VirtualMachine) int64 {
   342  			gasLimit := big.NewInt(vm.GetCurrentFrame().Locals[0])
   343  			addressPtr := uint32(vm.GetCurrentFrame().Locals[1])
   344  			i := 2
   345  			var valuePtr int
   346  			if field == "call" || field == "callCode" {
   347  				valuePtr = int(uint32(vm.GetCurrentFrame().Locals[i]))
   348  				i++
   349  			}
   350  			dataPtr := uint32(vm.GetCurrentFrame().Locals[i])
   351  			dataLen := uint32(vm.GetCurrentFrame().Locals[i+1])
   352  
   353  			// TODO: avoid panic? Or at least panic with coded out-of-bounds
   354  			target := crypto.MustAddressFromBytes(vm.Memory[addressPtr : addressPtr+crypto.AddressLength])
   355  
   356  			// TODO: is this guaranteed to be okay? Should be avoid panic here if out of bounds?
   357  			value := bin.BigIntFromLittleEndianBytes(vm.Memory[valuePtr : valuePtr+ValueByteSize])
   358  
   359  			var callType exec.CallType
   360  
   361  			switch field {
   362  			case "call":
   363  				callType = exec.CallTypeCall
   364  			case "callCode":
   365  				callType = exec.CallTypeCode
   366  			case "callStatic":
   367  				callType = exec.CallTypeStatic
   368  			case "callDeletegate":
   369  				callType = exec.CallTypeDelegate
   370  			default:
   371  				panic("should not happen")
   372  			}
   373  
   374  			var err error
   375  			ctx.returnData, err = engine.CallFromSite(ctx.state, ctx.vm.externalDispatcher, ctx.params,
   376  				engine.CallParams{
   377  					CallType: callType,
   378  					Callee:   target,
   379  					Input:    vm.Memory[dataPtr : dataPtr+dataLen],
   380  					Value:    *value,
   381  					Gas:      gasLimit,
   382  				})
   383  
   384  			// Refund any remaining gas to be used on subsequent calls
   385  			ctx.params.Gas.Add(ctx.params.Gas, gasLimit)
   386  
   387  			// TODO[Silas]: we may need to consider trapping and non-trapping errors here in a bit more of a principled way
   388  			//   (e.g. we may be currently handling things that should abort execution, it might be better to clasify
   389  			//   all of our coded errors as trapping (fatal abort WASM) or non-trapping (return error to WASM caller)
   390  			//   I'm not sure this is consistent in EVM either.
   391  			if err != nil {
   392  				if errors.GetCode(err) == errors.Codes.ExecutionReverted {
   393  					return Revert
   394  				}
   395  				// Spec says return 1 for error, but not sure when to do that (as opposed to abort):
   396  				// https://github.com/ewasm/design/blob/master/eth_interface.md#call
   397  				panic(err)
   398  			}
   399  			return Success
   400  		}
   401  
   402  	case "getCallDataSize":
   403  		return func(vm *lifeExec.VirtualMachine) int64 {
   404  			return int64(len(ctx.params.Input))
   405  		}
   406  
   407  	case "callDataCopy":
   408  		return func(vm *lifeExec.VirtualMachine) int64 {
   409  			destPtr := int(uint32(vm.GetCurrentFrame().Locals[0]))
   410  			dataOffset := int(uint32(vm.GetCurrentFrame().Locals[1]))
   411  			dataLen := int(uint32(vm.GetCurrentFrame().Locals[2]))
   412  
   413  			if dataLen > 0 {
   414  				copy(vm.Memory[destPtr:], ctx.params.Input[dataOffset:dataOffset+dataLen])
   415  			}
   416  
   417  			return Success
   418  		}
   419  
   420  	case "getReturnDataSize":
   421  		return func(vm *lifeExec.VirtualMachine) int64 {
   422  			return int64(len(ctx.returnData))
   423  		}
   424  
   425  	case "returnDataCopy":
   426  		return func(vm *lifeExec.VirtualMachine) int64 {
   427  			destPtr := int(uint32(vm.GetCurrentFrame().Locals[0]))
   428  			dataOffset := int(uint32(vm.GetCurrentFrame().Locals[1]))
   429  			dataLen := int(uint32(vm.GetCurrentFrame().Locals[2]))
   430  
   431  			if dataLen > 0 {
   432  				copy(vm.Memory[destPtr:], ctx.returnData[dataOffset:dataOffset+dataLen])
   433  			}
   434  
   435  			return Success
   436  		}
   437  
   438  	case "getCodeSize":
   439  		return func(vm *lifeExec.VirtualMachine) int64 {
   440  			return int64(len(ctx.code))
   441  		}
   442  
   443  	case "codeCopy":
   444  		return func(vm *lifeExec.VirtualMachine) int64 {
   445  			destPtr := int(uint32(vm.GetCurrentFrame().Locals[0]))
   446  			dataOffset := int(uint32(vm.GetCurrentFrame().Locals[1]))
   447  			dataLen := int(uint32(vm.GetCurrentFrame().Locals[2]))
   448  
   449  			if dataLen > 0 {
   450  				copy(vm.Memory[destPtr:], ctx.code[dataOffset:dataOffset+dataLen])
   451  			}
   452  
   453  			return Success
   454  		}
   455  
   456  	case "storageStore":
   457  		return func(vm *lifeExec.VirtualMachine) int64 {
   458  			keyPtr := int(uint32(vm.GetCurrentFrame().Locals[0]))
   459  			dataPtr := int(uint32(vm.GetCurrentFrame().Locals[1]))
   460  
   461  			key := bin.Word256{}
   462  			value := make([]byte, 32)
   463  
   464  			copy(key[:], vm.Memory[keyPtr:keyPtr+32])
   465  			copy(value, vm.Memory[dataPtr:dataPtr+32])
   466  
   467  			err := ctx.state.SetStorage(ctx.params.Callee, key, value)
   468  			if err != nil {
   469  				panic(err)
   470  			}
   471  			return Success
   472  		}
   473  
   474  	case "storageLoad":
   475  		return func(vm *lifeExec.VirtualMachine) int64 {
   476  
   477  			keyPtr := int(uint32(vm.GetCurrentFrame().Locals[0]))
   478  			dataPtr := int(uint32(vm.GetCurrentFrame().Locals[1]))
   479  
   480  			key := bin.Word256{}
   481  
   482  			copy(key[:], vm.Memory[keyPtr:keyPtr+32])
   483  
   484  			val, err := ctx.state.GetStorage(ctx.params.Callee, key)
   485  			if err != nil {
   486  				panic(err)
   487  			}
   488  			copy(vm.Memory[dataPtr:], val)
   489  
   490  			return Success
   491  		}
   492  
   493  	case "finish":
   494  		return func(vm *lifeExec.VirtualMachine) int64 {
   495  			dataPtr := int(uint32(vm.GetCurrentFrame().Locals[0]))
   496  			dataLen := int(uint32(vm.GetCurrentFrame().Locals[1]))
   497  
   498  			ctx.output = vm.Memory[dataPtr : dataPtr+dataLen]
   499  
   500  			panic(errors.Codes.None)
   501  		}
   502  
   503  	case "revert":
   504  		return func(vm *lifeExec.VirtualMachine) int64 {
   505  
   506  			dataPtr := int(uint32(vm.GetCurrentFrame().Locals[0]))
   507  			dataLen := int(uint32(vm.GetCurrentFrame().Locals[1]))
   508  
   509  			ctx.output = vm.Memory[dataPtr : dataPtr+dataLen]
   510  
   511  			panic(errors.Codes.ExecutionReverted)
   512  		}
   513  
   514  	case "getAddress":
   515  		return func(vm *lifeExec.VirtualMachine) int64 {
   516  			addressPtr := int(uint32(vm.GetCurrentFrame().Locals[0]))
   517  
   518  			copy(vm.Memory[addressPtr:], ctx.params.Callee.Bytes())
   519  
   520  			return Success
   521  		}
   522  
   523  	case "getCallValue":
   524  		return func(vm *lifeExec.VirtualMachine) int64 {
   525  			valuePtr := int(uint32(vm.GetCurrentFrame().Locals[0]))
   526  
   527  			// ewasm value is little endian 128 bit value
   528  			copy(vm.Memory[valuePtr:], bin.BigIntToLittleEndianBytes(&ctx.params.Value))
   529  
   530  			return Success
   531  		}
   532  
   533  	case "getExternalBalance":
   534  		return func(vm *lifeExec.VirtualMachine) int64 {
   535  			addressPtr := int(uint32(vm.GetCurrentFrame().Locals[0]))
   536  			balancePtr := int(uint32(vm.GetCurrentFrame().Locals[1]))
   537  
   538  			address := crypto.Address{}
   539  
   540  			copy(address[:], vm.Memory[addressPtr:addressPtr+crypto.AddressLength])
   541  			acc, err := ctx.state.GetAccount(address)
   542  			if err != nil {
   543  				panic(errors.Codes.InvalidAddress)
   544  			}
   545  
   546  			// ewasm value is little endian 128 bit value
   547  			bs := make([]byte, 16)
   548  			binary.LittleEndian.PutUint64(bs, acc.Balance)
   549  
   550  			copy(vm.Memory[balancePtr:], bs)
   551  
   552  			return Success
   553  		}
   554  
   555  	case "getBlockTimestamp":
   556  		return func(vm *lifeExec.VirtualMachine) int64 {
   557  			return int64(ctx.state.Blockchain.LastBlockTime().Unix())
   558  		}
   559  
   560  	case "getBlockNumber":
   561  		return func(vm *lifeExec.VirtualMachine) int64 {
   562  			return int64(ctx.state.Blockchain.LastBlockHeight())
   563  		}
   564  
   565  	case "getTxOrigin":
   566  		return func(vm *lifeExec.VirtualMachine) int64 {
   567  			addressPtr := int(uint32(vm.GetCurrentFrame().Locals[0]))
   568  
   569  			copy(vm.Memory[addressPtr:addressPtr+crypto.AddressLength], ctx.params.Origin.Bytes())
   570  
   571  			return Success
   572  		}
   573  
   574  	case "getCaller":
   575  		return func(vm *lifeExec.VirtualMachine) int64 {
   576  			addressPtr := int(uint32(vm.GetCurrentFrame().Locals[0]))
   577  
   578  			copy(vm.Memory[addressPtr:addressPtr+crypto.AddressLength], ctx.params.Caller.Bytes())
   579  
   580  			return Success
   581  		}
   582  
   583  	case "getBlockGasLimit":
   584  		return func(vm *lifeExec.VirtualMachine) int64 {
   585  			return ctx.params.Gas.Int64()
   586  		}
   587  
   588  	case "getGasLeft":
   589  		return func(vm *lifeExec.VirtualMachine) int64 {
   590  			// do the same as EVM
   591  			return ctx.params.Gas.Int64()
   592  		}
   593  
   594  	case "getBlockCoinbase":
   595  		return func(vm *lifeExec.VirtualMachine) int64 {
   596  			// do the same as EVM
   597  			addressPtr := int(uint32(vm.GetCurrentFrame().Locals[0]))
   598  
   599  			copy(vm.Memory[addressPtr:addressPtr+crypto.AddressLength], crypto.ZeroAddress.Bytes())
   600  
   601  			return Success
   602  		}
   603  
   604  	case "getBlockHash":
   605  		return func(vm *lifeExec.VirtualMachine) int64 {
   606  			blockNumber := uint64(vm.GetCurrentFrame().Locals[0])
   607  			hashPtr := int(vm.GetCurrentFrame().Locals[1])
   608  
   609  			lastBlockHeight := ctx.state.Blockchain.LastBlockHeight()
   610  			if blockNumber >= lastBlockHeight {
   611  				panic(fmt.Sprintf(" => attempted to get block hash of a non-existent block: %v", blockNumber))
   612  			} else if lastBlockHeight-blockNumber > evm.MaximumAllowedBlockLookBack {
   613  				panic(fmt.Sprintf(" => attempted to get block hash of a block %d outside of the allowed range "+
   614  					"(must be within %d blocks)", blockNumber, evm.MaximumAllowedBlockLookBack))
   615  			} else {
   616  				hash, err := ctx.state.Blockchain.BlockHash(blockNumber)
   617  				if err != nil {
   618  					panic(fmt.Sprintf(" => blockhash failed: %v", err))
   619  				}
   620  
   621  				copy(vm.Memory[hashPtr:hashPtr+len(hash)], hash)
   622  			}
   623  
   624  			return Success
   625  		}
   626  
   627  	case "log":
   628  		return func(vm *lifeExec.VirtualMachine) int64 {
   629  			dataPtr := int(uint32(vm.GetCurrentFrame().Locals[0]))
   630  			dataLen := int(uint32(vm.GetCurrentFrame().Locals[1]))
   631  
   632  			data := vm.Memory[dataPtr : dataPtr+dataLen]
   633  
   634  			topicCount := uint32(vm.GetCurrentFrame().Locals[2])
   635  			topics := make([]bin.Word256, topicCount)
   636  
   637  			if topicCount > 4 {
   638  				panic(fmt.Sprintf("%d topics not permitted", topicCount))
   639  			}
   640  
   641  			for i := uint32(0); i < topicCount; i++ {
   642  				topicPtr := int(uint32(vm.GetCurrentFrame().Locals[3+i]))
   643  				topicData := vm.Memory[topicPtr : topicPtr+bin.Word256Bytes]
   644  				topics[i] = bin.RightPadWord256(topicData)
   645  			}
   646  
   647  			err := ctx.state.EventSink.Log(&exec.LogEvent{
   648  				Address: ctx.params.Callee,
   649  				Topics:  topics,
   650  				Data:    data,
   651  			})
   652  
   653  			if err != nil {
   654  				panic(fmt.Sprintf(" => log failed: %v", err))
   655  			}
   656  
   657  			return Success
   658  		}
   659  
   660  	default:
   661  		panic(fmt.Sprintf("unknown function %s", field))
   662  	}
   663  }
   664  
   665  // When deploying wasm code, the abi encoded arguments to the constructor are added to the code. Wagon
   666  // does not like seeing this data, so strip this off. We have to walk the wasm format to the last section
   667  
   668  // There might be a better solution to this.
   669  func wasmSize(code []byte) int64 {
   670  	reader := bytes.NewReader(code)
   671  	top := int64(8)
   672  	for {
   673  		reader.Seek(top, 0)
   674  		id, err := reader.ReadByte()
   675  		if err != nil || id == 0 || id > 11 {
   676  			// invalid section id
   677  			break
   678  		}
   679  		size, err := leb128.ReadVarUint32(reader)
   680  		if err != nil {
   681  			break
   682  		}
   683  		pos, _ := reader.Seek(0, 1)
   684  		if pos+int64(size) > int64(len(code)) {
   685  			break
   686  		}
   687  		top = pos + int64(size)
   688  	}
   689  
   690  	return top
   691  }