Skip to content

Commit

Permalink
Merge pull request #2884 from dessaya/evm-fixes
Browse files Browse the repository at this point in the history
Misc EVM fixes
  • Loading branch information
dessaya authored Sep 21, 2023
2 parents d0a660f + c8bee5c commit 90a9fd0
Show file tree
Hide file tree
Showing 25 changed files with 361 additions and 108 deletions.
10 changes: 9 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ compile-solidity:
build-cli:
cd tools/wasp-cli && go mod tidy && go build -ldflags $(BUILD_LD_FLAGS) -o ../../

# use like: make build-tool TOOL=./tools/dbinspector
build-tool:
$(BUILD_CMD) $(TOOL)

build-full: build-cli
$(BUILD_CMD) ./...

Expand All @@ -53,6 +57,10 @@ test-short:
install-cli:
cd tools/wasp-cli && go mod tidy && go install -ldflags $(BUILD_LD_FLAGS)

# use like: make install-tool TOOL=./tools/dbinspector
install-tool:
$(INSTALL_CMD) $(TOOL)

install-full: install-cli
$(INSTALL_CMD) ./...

Expand Down Expand Up @@ -103,4 +111,4 @@ deps-versions:
awk -F ":" '{ print $$1 }' | \
{ read from ; read to; awk -v s="$$from" -v e="$$to" 'NR>1*s&&NR<1*e' packages/testutil/privtangle/privtangle.go; }

.PHONY: all wasm compile-solidity build-cli build-full build build-lint test-full test test-short install-cli install-full install lint gofumpt-list docker-build deps-versions
.PHONY: all wasm compile-solidity build-tool install-tool build-cli build-full build build-lint test-full test test-short install-cli install-full install lint gofumpt-list docker-build deps-versions
4 changes: 2 additions & 2 deletions components/logger/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package logger

import (
"github.com/iotaledger/hive.go/app"
"github.com/iotaledger/wasp/packages/evm/evmlogger"
)

func init() {
Expand All @@ -14,7 +15,6 @@ func init() {
var Component *app.Component

func configure() error {
initGoEthLogger(Component.App().NewLogger("go-ethereum"))

evmlogger.Init(Component.App().NewLogger("go-ethereum"))
return nil
}
2 changes: 1 addition & 1 deletion packages/chain/mempool/typed_pool_by_nonce.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ func (p *TypedPoolByNonce[V]) Add(request V) {
}

defer func() {
p.log.Debugf("ADD %v as key=%v, senderAccount: ", request.ID(), ref, account)
p.log.Debugf("ADD %v as key=%v, senderAccount: %s", request.ID(), ref, account)
p.sizeMetric(p.refLUT.Size())
p.waitReq.MarkAvailable(request)
}()
Expand Down
54 changes: 33 additions & 21 deletions packages/chainutil/evmestimategas.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (
"github.com/iotaledger/wasp/packages/vm/gas"
)

var evmErrorsRegex = regexp.MustCompile("out of gas|intrinsic gas too low|(execution reverted$)")
var evmErrOutOfGasRegex = regexp.MustCompile("out of gas|intrinsic gas too low")

// EVMEstimateGas executes the given request and discards the resulting chain state. It is useful
// for estimating gas.
Expand All @@ -39,29 +39,14 @@ func EVMEstimateGas(ch chain.ChainCore, aliasOutput *isc.AliasOutputWithID, call

// Create a helper to check if a gas allowance results in an executable transaction
blockTime := time.Now()
executable := func(gas uint64) (failed bool, used uint64, err error) {
executable := func(gas uint64) (failed bool, result *vm.RequestResult, err error) {
call.Gas = gas
iscReq := isc.NewEVMOffLedgerCallRequest(ch.ID(), call)
res, err := runISCRequest(ch, aliasOutput, blockTime, iscReq, true)
if err != nil {
return true, 0, err
return true, nil, err
}
if res.Receipt.Error != nil {
if res.Receipt.Error.ErrorCode == vm.ErrGasBudgetExceeded.Code() {
// out of gas when charging ISC gas
return true, 0, nil
}
vmerr, resolvingErr := ResolveError(ch, res.Receipt.Error)
if resolvingErr != nil {
panic(fmt.Errorf("error resolving vmerror %w", resolvingErr))
}
if evmErrorsRegex.Match([]byte(vmerr.Error())) {
// increase gas
return true, 0, nil
}
return true, 0, vmerr
}
return false, res.Receipt.GasBurned, nil
return res.Receipt.Error != nil, res, nil
}

// Execute the binary search and hone in on an executable gas limit
Expand All @@ -83,13 +68,15 @@ func EVMEstimateGas(ch chain.ChainCore, aliasOutput *isc.AliasOutputWithID, call

var failed bool
var err error
failed, lastUsed, err = executable(mid)
failed, res, err := executable(mid)
if err != nil {
return 0, err
}
if failed {
lastUsed = 0
lo = mid
} else {
lastUsed = res.Receipt.GasBurned
hi = mid
if lastUsed == mid {
// if used gas == gas limit, then use this as the estimation.
Expand All @@ -103,11 +90,20 @@ func EVMEstimateGas(ch chain.ChainCore, aliasOutput *isc.AliasOutputWithID, call

// Reject the transaction as invalid if it still fails at the highest allowance
if hi == gasCap {
failed, _, err := executable(hi)
failed, res, err := executable(hi)
if err != nil {
return 0, err
}
if failed {
if res.Receipt.Error != nil {
isOutOfGas, resolvedErr, err := resolveError(ch, res.Receipt.Error)
if err != nil {
return 0, err
}
if resolvedErr != nil && !isOutOfGas {
return 0, resolvedErr
}
}
if hi == maximumPossibleGas {
return 0, fmt.Errorf("request might require more gas than it is allowed by the VM (%d), or will never succeed", gasCap)
}
Expand All @@ -122,3 +118,19 @@ func getMaxCallGasLimit(ch chain.ChainCore) uint64 {
info := governance.NewStateAccess(mustLatestState(ch)).ChainInfo(ch.ID())
return gas.EVMCallGasLimit(info.GasLimits, &info.GasFeePolicy.EVMGasRatio)
}

func resolveError(ch chain.ChainCore, receiptError *isc.UnresolvedVMError) (isOutOfGas bool, resolved *isc.VMError, err error) {
if receiptError.ErrorCode == vm.ErrGasBudgetExceeded.Code() {
// out of gas when charging ISC gas
return true, nil, nil
}
vmerr, resolvingErr := ResolveError(ch, receiptError)
if resolvingErr != nil {
return true, nil, fmt.Errorf("error resolving vmerror: %w", resolvingErr)
}
if evmErrOutOfGasRegex.Match([]byte(vmerr.Error())) {
// increase gas
return true, vmerr, nil
}
return false, vmerr, nil
}
15 changes: 10 additions & 5 deletions components/logger/evm.go → packages/evm/evmlogger/evmlogger.go
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
package logger
package evmlogger

import (
"strings"

"github.com/ethereum/go-ethereum/log"

"github.com/iotaledger/hive.go/logger"
)

func initGoEthLogger(waspLogger *logger.Logger) {
var format = log.TerminalFormat(false)

func Init(waspLogger *logger.Logger) {
log.Root().SetHandler(log.FuncHandler(func(r *log.Record) error {
s := strings.TrimRight(string(format.Format(r)), "\n")
switch r.Lvl {
case log.LvlCrit, log.LvlError:
waspLogger.Errorf("[%s] %s", r.Lvl.AlignedString(), r.Msg)
waspLogger.Error(s)
case log.LvlTrace, log.LvlDebug:
waspLogger.Debugf("[%s] %s", r.Lvl.AlignedString(), r.Msg)
waspLogger.Debug(s)
default:
waspLogger.Infof("[%s] %s", r.Lvl.AlignedString(), r.Msg)
waspLogger.Info(s)
}
return nil
}))
Expand Down
19 changes: 0 additions & 19 deletions packages/evm/evmtest/env.go

This file was deleted.

11 changes: 4 additions & 7 deletions packages/evm/jsonrpc/accounts.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ import (

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"golang.org/x/exp/slices"
)

type AccountManager struct {
accounts map[common.Address]*ecdsa.PrivateKey
addrs []common.Address
}

func NewAccountManager(accounts []*ecdsa.PrivateKey) *AccountManager {
Expand All @@ -30,18 +32,13 @@ func (a *AccountManager) Add(keyPair *ecdsa.PrivateKey) {
return
}
a.accounts[addr] = keyPair
a.addrs = append(a.addrs, addr)
}

func (a *AccountManager) Get(addr common.Address) *ecdsa.PrivateKey {
return a.accounts[addr]
}

func (a *AccountManager) Addresses() []common.Address {
ret := make([]common.Address, len(a.accounts))
i := 0
for addr := range a.accounts {
ret[i] = addr
i++
}
return ret
return slices.Clone(a.addrs)
}
2 changes: 2 additions & 0 deletions packages/evm/jsonrpc/chainbackend.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,6 @@ type ChainBackend interface {
ISCStateByBlockIndex(blockIndex uint32) (state.State, error)
ISCStateByTrieRoot(trieRoot trie.Hash) (state.State, error)
BaseToken() *parameters.BaseToken
TakeSnapshot() (int, error)
RevertToSnapshot(int) error
}
2 changes: 0 additions & 2 deletions packages/evm/jsonrpc/jsonrpctest/jsonrpc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,6 @@ type soloTestEnv struct {
}

func newSoloTestEnv(t testing.TB) *soloTestEnv {
evmtest.InitGoEthLogger(t)

var log *logger.Logger
if _, ok := t.(*testing.B); ok {
log = testlogger.NewSilentLogger(t.Name(), true)
Expand Down
1 change: 1 addition & 0 deletions packages/evm/jsonrpc/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ func NewServer(
{"eth", NewEthService(evmChain, accountManager, metrics)},
{"debug", NewDebugService(evmChain, metrics)},
{"txpool", NewTxPoolService()},
{"evm", NewEVMService(evmChain)},
} {
err := rpcsrv.RegisterName(srv.namespace, srv.service)
if err != nil {
Expand Down
19 changes: 19 additions & 0 deletions packages/evm/jsonrpc/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -684,3 +684,22 @@ func (d *DebugService) TraceTransaction(txHash common.Hash, config *tracers.Trac
},
)
}

type EVMService struct {
evmChain *EVMChain
}

func NewEVMService(evmChain *EVMChain) *EVMService {
return &EVMService{
evmChain: evmChain,
}
}

func (e *EVMService) Snapshot() (hexutil.Uint, error) {
n, err := e.evmChain.backend.TakeSnapshot()
return hexutil.Uint(n), err
}

func (e *EVMService) Revert(snapshot hexutil.Uint) error {
return e.evmChain.backend.RevertToSnapshot(int(snapshot))
}
11 changes: 11 additions & 0 deletions packages/evm/jsonrpc/waspevmbackend.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package jsonrpc

import (
"errors"
"fmt"
"time"

Expand Down Expand Up @@ -141,3 +142,13 @@ func (b *WaspEVMBackend) ISCChainID() *isc.ChainID {
chID := b.chain.ID()
return &chID
}

var errNotImplemented = errors.New("method not implemented")

func (*WaspEVMBackend) RevertToSnapshot(int) error {
return errNotImplemented
}

func (*WaspEVMBackend) TakeSnapshot() (int, error) {
return 0, errNotImplemented
}
4 changes: 2 additions & 2 deletions packages/metrics/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ func NewChainMetricsProvider() *ChainMetricsProvider {
StateManager: newChainStateManagerMetricsProvider(),
Snapshots: newChainSnapshotsMetricsProvider(),
NodeConn: newChainNodeConnMetricsProvider(),
WebAPI: newChainWebAPIMetricsProvider(),
WebAPI: NewChainWebAPIMetricsProvider(),
State: newChainStateMetricsProvider(),
}
}
Expand Down Expand Up @@ -91,7 +91,7 @@ func (m *ChainMetricsProvider) GetChainMetrics(chainID isc.ChainID) *ChainMetric
StateManager: m.StateManager.createForChain(chainID),
Snapshots: m.Snapshots.createForChain(chainID),
NodeConn: m.NodeConn.createForChain(chainID),
WebAPI: m.WebAPI.createForChain(chainID),
WebAPI: m.WebAPI.CreateForChain(chainID),
State: m.State.createForChain(chainID),
}
m.chains[chainID] = cm
Expand Down
4 changes: 2 additions & 2 deletions packages/metrics/chain_webapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ type ChainWebAPIMetricsProvider struct {
evmRPCCalls *prometheus.HistogramVec
}

func newChainWebAPIMetricsProvider() *ChainWebAPIMetricsProvider {
func NewChainWebAPIMetricsProvider() *ChainWebAPIMetricsProvider {
return &ChainWebAPIMetricsProvider{
requests: prometheus.NewHistogramVec(prometheus.HistogramOpts{
Namespace: "iota_wasp",
Expand All @@ -40,7 +40,7 @@ func (p *ChainWebAPIMetricsProvider) register(reg prometheus.Registerer) {
)
}

func (p *ChainWebAPIMetricsProvider) createForChain(chainID isc.ChainID) *ChainWebAPIMetrics {
func (p *ChainWebAPIMetricsProvider) CreateForChain(chainID isc.ChainID) *ChainWebAPIMetrics {
return newChainWebAPIMetrics(p, chainID)
}

Expand Down
4 changes: 2 additions & 2 deletions packages/parameters/l1parameters.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ var (
l1ParamsMutex = &sync.RWMutex{}
l1Params *L1Params

l1ForTesting = &L1Params{
L1ForTesting = &L1Params{
// There are no limits on how big from a size perspective an essence can be, so it is just derived from 32KB - Message fields without payload = max size of the payload
MaxPayloadSize: MaxPayloadSize,
Protocol: &iotago.ProtocolParameters{
Expand Down Expand Up @@ -80,7 +80,7 @@ func L1() *L1Params {
func L1NoLock() *L1Params {
if l1Params == nil {
if isTestContext() {
l1Params = l1ForTesting
l1Params = L1ForTesting
} else if l1ParamsLazyInit != nil {
l1ParamsLazyInit()
}
Expand Down
12 changes: 12 additions & 0 deletions packages/solo/context.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package solo

import "github.com/stretchr/testify/require"

type Context interface {
require.TestingT
Name() string
Cleanup(func())
Helper()
Logf(string, ...any)
Fatalf(string, ...any)
}
Loading

0 comments on commit 90a9fd0

Please sign in to comment.