Skip to content

Commit

Permalink
[evm] snapshot gas refund (#3637)
Browse files Browse the repository at this point in the history
  • Loading branch information
dustinxie committed Sep 20, 2022
1 parent e9732a1 commit 8a3e37a
Show file tree
Hide file tree
Showing 9 changed files with 132 additions and 62 deletions.
2 changes: 2 additions & 0 deletions action/protocol/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ type (
CreateLegacyNonceAccount bool
FixGasAndNonceUpdate bool
FixUnproductiveDelegates bool
CorrectGasRefund bool
}

// FeatureWithHeightCtx provides feature check functions.
Expand Down Expand Up @@ -240,6 +241,7 @@ func WithFeatureCtx(ctx context.Context) context.Context {
CreateLegacyNonceAccount: !g.IsOkhotsk(height),
FixGasAndNonceUpdate: g.IsOkhotsk(height),
FixUnproductiveDelegates: g.IsOkhotsk(height),
CorrectGasRefund: g.IsOkhotsk(height),
},
)
}
Expand Down
18 changes: 17 additions & 1 deletion action/protocol/execution/evm/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,9 @@ func prepareStateDB(ctx context.Context, sm protocol.StateManager) (*StateDBAdap
if !featureCtx.FixUnproductiveDelegates {
opts = append(opts, NotCheckPutStateErrorOption())
}
if !featureCtx.CorrectGasRefund {
opts = append(opts, NotCorrectGasRefundOption())
}

return NewStateDBAdapter(
sm,
Expand Down Expand Up @@ -373,7 +376,7 @@ func executeInEVM(ctx context.Context, evmParams *Params, stateDB *StateDBAdapte
remainingGas -= intriGas

// Set up the initial access list
if rules := chainConfig.Rules(evm.Context.BlockNumber); rules.IsBerlin {
if rules := chainConfig.Rules(evm.Context.BlockNumber, false); rules.IsBerlin {
stateDB.PrepareAccessList(evmParams.txCtx.Origin, evmParams.contract, vm.ActivePrecompiles(rules), evmParams.accessList)
}
var (
Expand Down Expand Up @@ -418,6 +421,19 @@ func executeInEVM(ctx context.Context, evmParams *Params, stateDB *StateDBAdapte
// After EIP-3529: refunds are capped to gasUsed / 5
refund = (evmParams.gas - remainingGas) / params.RefundQuotientEIP3529
}
// adjust refund due to dynamicGas
var (
refundLastSnapshot = stateDB.RefundAtLastSnapshot()
currentRefund = stateDB.GetRefund()
featureCtx = protocol.MustGetFeatureCtx(ctx)
)
if evmErr != nil && !featureCtx.CorrectGasRefund && refundLastSnapshot > 0 && refundLastSnapshot != currentRefund {
if refundLastSnapshot > currentRefund {
stateDB.AddRefund(refundLastSnapshot - currentRefund)
} else {
stateDB.SubRefund(currentRefund - refundLastSnapshot)
}
}
if refund > stateDB.GetRefund() {
refund = stateDB.GetRefund()
}
Expand Down
19 changes: 8 additions & 11 deletions action/protocol/execution/evm/evm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ func TestConstantinople(t *testing.T) {
require.True(evmChainConfig.IsPetersburg(evm.Context.BlockNumber))

// verify chainRules
chainRules := evmChainConfig.Rules(ps.context.BlockNumber)
chainRules := evmChainConfig.Rules(ps.context.BlockNumber, false)
require.Equal(g.IsGreenland(e.height), chainRules.IsHomestead)
require.Equal(g.IsGreenland(e.height), chainRules.IsEIP150)
require.Equal(g.IsGreenland(e.height), chainRules.IsEIP158)
Expand All @@ -273,7 +273,6 @@ func TestConstantinople(t *testing.T) {
// verify iotex configs in chain config block
require.Equal(big.NewInt(int64(g.BeringBlockHeight)), evmChainConfig.BeringBlock)
require.Equal(big.NewInt(int64(g.GreenlandBlockHeight)), evmChainConfig.GreenlandBlock)
require.Equal(!g.IsBering(e.height), evm.IsPreBering())

// iceland = support chainID + enable Istanbul and Muir Glacier
isIceland := g.IsIceland(e.height)
Expand All @@ -288,15 +287,13 @@ func TestConstantinople(t *testing.T) {
require.Equal(isIceland, evmChainConfig.IsMuirGlacier(evm.Context.BlockNumber))
require.Equal(isIceland, chainRules.IsIstanbul)

// enable Berlin and London
isBerlin := g.IsOkhotsk(e.height)
require.Equal(isBerlin, evmChainConfig.IsBerlin(evm.Context.BlockNumber))
require.Equal(isBerlin, chainRules.IsBerlin)
isLondon := g.IsOkhotsk(e.height)
require.Equal(isLondon, evmChainConfig.IsLondon(evm.Context.BlockNumber))
require.Equal(isLondon, chainRules.IsLondon)
require.False(evmChainConfig.IsCatalyst(evm.Context.BlockNumber))
require.False(chainRules.IsCatalyst)
// Okhotsk = enable Berlin and London
isOkhotsk := g.IsOkhotsk(e.height)
require.Equal(isOkhotsk, evmChainConfig.IsBerlin(evm.Context.BlockNumber))
require.Equal(isOkhotsk, chainRules.IsBerlin)
require.Equal(isOkhotsk, evmChainConfig.IsLondon(evm.Context.BlockNumber))
require.Equal(isOkhotsk, chainRules.IsLondon)
require.False(chainRules.IsMerge)
}
}

Expand Down
28 changes: 28 additions & 0 deletions action/protocol/execution/evm/evmstatedbadapter.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ type (
blockHeight uint64
executionHash hash.Hash256
refund uint64
refundAtLastSnapshot uint64
refundSnapshot map[int]uint64
cachedContract contractMap
contractSnapshot map[int]contractMap // snapshots of contracts
suicided deleteAccount // account/contract calling Suicide
Expand All @@ -74,6 +76,7 @@ type (
fixSnapshotOrder bool
revertLog bool
notCheckPutStateError bool
notCorrectGasRefund bool
}
)

Expand Down Expand Up @@ -144,6 +147,14 @@ func NotCheckPutStateErrorOption() StateDBAdapterOption {
}
}

// NotCorrectGasRefundOption set correctGasRefund as true
func NotCorrectGasRefundOption() StateDBAdapterOption {
return func(adapter *StateDBAdapter) error {
adapter.notCorrectGasRefund = true
return nil
}
}

// NewStateDBAdapter creates a new state db with iotex blockchain
func NewStateDBAdapter(
sm protocol.StateManager,
Expand All @@ -157,6 +168,7 @@ func NewStateDBAdapter(
err: nil,
blockHeight: blockHeight,
executionHash: executionHash,
refundSnapshot: make(map[int]uint64),
cachedContract: make(contractMap),
contractSnapshot: make(map[int]contractMap),
suicided: make(deleteAccount),
Expand Down Expand Up @@ -540,6 +552,13 @@ func (stateDB *StateDBAdapter) RevertToSnapshot(snapshot int) {
log.L().Error("Failed to get snapshot.", zap.Int("snapshot", snapshot))
return
}
// restore gas refund
if stateDB.notCorrectGasRefund {
// check if refund has changed from last snapshot (due to dynamicGas)
stateDB.refundAtLastSnapshot = stateDB.refundSnapshot[snapshot]
} else {
stateDB.refund = stateDB.refundSnapshot[snapshot]
}
// restore logs and txLogs
if stateDB.revertLog {
stateDB.logs = stateDB.logs[:stateDB.logsSnapshot[snapshot]]
Expand Down Expand Up @@ -622,6 +641,11 @@ func (stateDB *StateDBAdapter) RevertToSnapshot(snapshot int) {
}
}

// RefundAtLastSnapshot returns refund at last snapshot
func (stateDB *StateDBAdapter) RefundAtLastSnapshot() uint64 {
return stateDB.refundAtLastSnapshot
}

func (stateDB *StateDBAdapter) cachedContractAddrs() []hash.Hash160 {
addrs := make([]hash.Hash160, 0, len(stateDB.cachedContract))
for addr := range stateDB.cachedContract {
Expand Down Expand Up @@ -653,6 +677,8 @@ func (stateDB *StateDBAdapter) Snapshot() int {
// stateDB.err = err
return sn
}
// record the current gas refund
stateDB.refundSnapshot[sn] = stateDB.refund
// record the current log size
if stateDB.revertLog {
stateDB.logsSnapshot[sn] = len(stateDB.logs)
Expand Down Expand Up @@ -1010,6 +1036,8 @@ func (stateDB *StateDBAdapter) getNewContract(addr hash.Hash160) (Contract, erro

// clear clears local changes
func (stateDB *StateDBAdapter) clear() {
stateDB.refundAtLastSnapshot = 0
stateDB.refundSnapshot = make(map[int]uint64)
stateDB.cachedContract = make(contractMap)
stateDB.contractSnapshot = make(map[int]contractMap)
stateDB.suicided = make(deleteAccount)
Expand Down
10 changes: 10 additions & 0 deletions action/protocol/execution/evm/evmstatedbadapter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,7 @@ var tests = []stateDBTest{
{_c1, _k2, _v2},
{_c3, _k3, _v4},
},
15000,
[]sui{
{_c2, false, false},
{_c4, false, false},
Expand Down Expand Up @@ -344,6 +345,7 @@ var tests = []stateDBTest{
{_c2, _k3, _v3},
{_c2, _k4, _v4},
},
2000,
[]sui{
{_c1, true, true},
{_c3, true, true},
Expand Down Expand Up @@ -371,6 +373,7 @@ var tests = []stateDBTest{
{_c2, _k3, _v1},
{_c2, _k4, _v2},
},
15000,
[]sui{
{_addr1, true, true},
},
Expand Down Expand Up @@ -426,6 +429,8 @@ func TestSnapshotRevertAndCommit(t *testing.T) {
for _, e := range test.states {
stateDB.SetState(e.addr, e.k, e.v)
}
// set refund
stateDB.refund = test.refund
// set suicide
for _, e := range test.suicide {
require.Equal(e.suicide, stateDB.Suicide(e.addr))
Expand Down Expand Up @@ -480,6 +485,7 @@ func TestSnapshotRevertAndCommit(t *testing.T) {
{_c2, _k3, _v1},
{_c2, _k4, _v2},
},
tests[2].refund,
[]sui{
{_c1, true, true},
{_c3, true, true},
Expand Down Expand Up @@ -507,6 +513,7 @@ func TestSnapshotRevertAndCommit(t *testing.T) {
},
[]code{},
tests[1].states,
tests[1].refund,
[]sui{
{_c1, true, true},
{_c3, true, true},
Expand Down Expand Up @@ -538,6 +545,7 @@ func TestSnapshotRevertAndCommit(t *testing.T) {
{_c1, _k2, _v2},
{_c3, _k3, _v4},
},
tests[0].refund,
[]sui{
{_c1, false, true},
{_c3, false, true},
Expand Down Expand Up @@ -580,6 +588,8 @@ func TestSnapshotRevertAndCommit(t *testing.T) {
for _, e := range test.states {
require.Equal(e.v, stateDB.GetState(e.addr, e.k))
}
// test refund
require.Equal(test.refund, stateDB.refund)
// test preimage
for _, e := range test.preimage {
v := stateDB.preimages[e.hash]
Expand Down
1 change: 1 addition & 0 deletions action/protocol/execution/evm/testdata_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ type (
balance []bal
codes []code
states []evmSet
refund uint64
suicide []sui
preimage []image
accessList []access
Expand Down
3 changes: 2 additions & 1 deletion api/grpcserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"time"

"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/tracers/logger"
grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
grpc_recovery "github.com/grpc-ecosystem/go-grpc-middleware/recovery"
grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
Expand Down Expand Up @@ -671,7 +672,7 @@ func (svr *gRPCHandler) TraceTransactionStructLogs(ctx context.Context, in *iote
if !ok {
return nil, status.Error(codes.InvalidArgument, "the type of action is not supported")
}
tracer := vm.NewStructLogger(nil)
tracer := logger.NewStructLogger(nil)
ctx = protocol.WithVMConfigCtx(ctx, vm.Config{
Debug: true,
Tracer: tracer,
Expand Down
24 changes: 13 additions & 11 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ go 1.18

require (
github.com/cenkalti/backoff v2.2.1+incompatible
github.com/ethereum/go-ethereum v1.10.4
github.com/ethereum/go-ethereum v1.10.21
github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a
github.com/go-redis/redis/v8 v8.11.4
github.com/go-sql-driver/mysql v1.4.1
github.com/golang/mock v1.4.4
github.com/golang/snappy v0.0.3
github.com/golang/protobuf v1.5.2
github.com/golang/snappy v0.0.4
github.com/gorilla/websocket v1.4.2
github.com/grpc-ecosystem/go-grpc-middleware v1.2.0
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
Expand Down Expand Up @@ -45,20 +46,19 @@ require (
go.uber.org/automaxprocs v1.2.0
go.uber.org/config v1.3.1
go.uber.org/zap v1.16.0
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519
golang.org/x/net v0.0.0-20220607020251-c690dde0001d
golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde
google.golang.org/genproto v0.0.0-20211223182754-3ac035c7e7cb
google.golang.org/grpc v1.43.0
google.golang.org/protobuf v1.28.1
gopkg.in/yaml.v2 v2.4.0
)

require (
github.com/golang/protobuf v1.5.2
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible
github.com/shirou/gopsutil/v3 v3.22.2
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.34.0
golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde
golang.org/x/text v0.3.7
)

Expand All @@ -67,11 +67,13 @@ require (
github.com/benbjohnson/clock v1.0.3 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/btcsuite/btcd v0.21.0-beta // indirect
github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect
github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect
github.com/deckarep/golang-set v1.7.1 // indirect
github.com/deckarep/golang-set v1.8.0 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/dustinxie/gmsm v1.4.0 // indirect
github.com/felixge/httpsnoop v1.0.3 // indirect
Expand All @@ -83,7 +85,7 @@ require (
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/google/gopacket v1.1.19 // indirect
github.com/google/uuid v1.1.5 // indirect
github.com/google/uuid v1.2.0 // indirect
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
github.com/hashicorp/errwrap v1.0.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.1 // indirect
Expand All @@ -95,7 +97,7 @@ require (
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/hashicorp/vault/sdk v0.1.14-0.20200519221838-e0cfd64bc267 // indirect
github.com/holiman/uint256 v1.2.0 // indirect
github.com/huin/goupnp v1.0.2 // indirect
github.com/huin/goupnp v1.0.3 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/ipfs/go-cid v0.0.7 // indirect
github.com/ipfs/go-datastore v0.4.5 // indirect
Expand Down Expand Up @@ -158,7 +160,7 @@ require (
github.com/minio/sha256-simd v1.0.0 // indirect
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/mapstructure v1.3.2 // indirect
github.com/mitchellh/mapstructure v1.4.1 // indirect
github.com/mr-tron/base58 v1.2.0 // indirect
github.com/multiformats/go-base32 v0.0.3 // indirect
github.com/multiformats/go-base36 v0.1.0 // indirect
Expand Down Expand Up @@ -201,6 +203,6 @@ require (
gopkg.in/yaml.v3 v3.0.1 // indirect
)

replace github.com/ethereum/go-ethereum => github.com/iotexproject/go-ethereum v1.7.4-0.20220515212948-4dae4279da68
replace github.com/ethereum/go-ethereum => github.com/iotexproject/go-ethereum v1.7.4-0.20220812094342-dcc10c72d176

replace golang.org/x/xerrors => golang.org/x/xerrors v0.0.0-20190212162355-a5947ffaace3
Loading

0 comments on commit 8a3e37a

Please sign in to comment.