forked from cerc-io/laconicd-deprecated
update fork
This commit is contained in:
@@ -4,21 +4,21 @@ import (
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"cosmossdk.io/math"
|
||||
sdkmath "cosmossdk.io/math"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
authante "github.com/cosmos/cosmos-sdk/x/auth/ante"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
|
||||
ethermint "github.com/cerc-io/laconicd/types"
|
||||
"github.com/cerc-io/laconicd/x/evm/types"
|
||||
ante "github.com/cosmos/cosmos-sdk/x/auth/ante"
|
||||
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||
)
|
||||
|
||||
func SetupContract(b *testing.B) (*KeeperTestSuite, common.Address) {
|
||||
suite := KeeperTestSuite{}
|
||||
suite.DoSetupTest(b)
|
||||
suite.SetupTest()
|
||||
|
||||
amt := sdk.Coins{ethermint.NewPhotonCoinInt64(1000000000000000000)}
|
||||
err := suite.app.BankKeeper.MintCoins(suite.ctx, types.ModuleName, amt)
|
||||
@@ -26,7 +26,7 @@ func SetupContract(b *testing.B) (*KeeperTestSuite, common.Address) {
|
||||
err = suite.app.BankKeeper.SendCoinsFromModuleToAccount(suite.ctx, types.ModuleName, suite.address.Bytes(), amt)
|
||||
require.NoError(b, err)
|
||||
|
||||
contractAddr := suite.DeployTestContract(b, suite.address, math.NewIntWithDecimal(1000, 18).BigInt())
|
||||
contractAddr := suite.DeployTestContract(b, suite.address, sdkmath.NewIntWithDecimal(1000, 18).BigInt())
|
||||
suite.Commit()
|
||||
|
||||
return &suite, contractAddr
|
||||
@@ -34,7 +34,7 @@ func SetupContract(b *testing.B) (*KeeperTestSuite, common.Address) {
|
||||
|
||||
func SetupTestMessageCall(b *testing.B) (*KeeperTestSuite, common.Address) {
|
||||
suite := KeeperTestSuite{}
|
||||
suite.DoSetupTest(b)
|
||||
suite.SetupTest()
|
||||
|
||||
amt := sdk.Coins{ethermint.NewPhotonCoinInt64(1000000000000000000)}
|
||||
err := suite.app.BankKeeper.MintCoins(suite.ctx, types.ModuleName, amt)
|
||||
@@ -67,8 +67,8 @@ func DoBenchmark(b *testing.B, txBuilder TxBuilder) {
|
||||
txData, err := types.UnpackTxData(msg.Data)
|
||||
require.NoError(b, err)
|
||||
|
||||
fees := sdk.Coins{sdk.NewCoin(suite.EvmDenom(), sdk.NewIntFromBigInt(txData.Fee()))}
|
||||
err = ante.DeductFees(suite.app.BankKeeper, suite.ctx, suite.app.AccountKeeper.GetAccount(ctx, msg.GetFrom()), fees)
|
||||
fees := sdk.Coins{sdk.NewCoin(suite.EvmDenom(), sdkmath.NewIntFromBigInt(txData.Fee()))}
|
||||
err = authante.DeductFees(suite.app.BankKeeper, suite.ctx, suite.app.AccountKeeper.GetAccount(ctx, msg.GetFrom()), fees)
|
||||
require.NoError(b, err)
|
||||
|
||||
rsp, err := suite.app.EvmKeeper.EthereumTx(sdk.WrapSDKContext(ctx), msg)
|
||||
@@ -134,8 +134,8 @@ func BenchmarkMessageCall(b *testing.B) {
|
||||
txData, err := types.UnpackTxData(msg.Data)
|
||||
require.NoError(b, err)
|
||||
|
||||
fees := sdk.Coins{sdk.NewCoin(suite.EvmDenom(), sdk.NewIntFromBigInt(txData.Fee()))}
|
||||
err = ante.DeductFees(suite.app.BankKeeper, suite.ctx, suite.app.AccountKeeper.GetAccount(ctx, msg.GetFrom()), fees)
|
||||
fees := sdk.Coins{sdk.NewCoin(suite.EvmDenom(), sdkmath.NewIntFromBigInt(txData.Fee()))}
|
||||
err = authante.DeductFees(suite.app.BankKeeper, suite.ctx, suite.app.AccountKeeper.GetAccount(ctx, msg.GetFrom()), fees)
|
||||
require.NoError(b, err)
|
||||
|
||||
rsp, err := suite.app.EvmKeeper.EthereumTx(sdk.WrapSDKContext(ctx), msg)
|
||||
|
||||
+85
-74
@@ -15,6 +15,7 @@ import (
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
sdkmath "cosmossdk.io/math"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
@@ -88,6 +89,7 @@ func (k Keeper) CosmosAccount(c context.Context, req *types.QueryCosmosAccountRe
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
// ValidatorAccount implements the Query/Balance gRPC method
|
||||
func (k Keeper) ValidatorAccount(c context.Context, req *types.QueryValidatorAccountRequest) (*types.QueryValidatorAccountResponse, error) {
|
||||
if req == nil {
|
||||
return nil, status.Error(codes.InvalidArgument, "empty request")
|
||||
@@ -104,7 +106,7 @@ func (k Keeper) ValidatorAccount(c context.Context, req *types.QueryValidatorAcc
|
||||
|
||||
validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, consAddr)
|
||||
if !found {
|
||||
return nil, nil
|
||||
return nil, fmt.Errorf("validator not found for %s", consAddr.String())
|
||||
}
|
||||
|
||||
accAddr := sdk.AccAddress(validator.GetOperator())
|
||||
@@ -362,8 +364,15 @@ func (k Keeper) TraceTx(c context.Context, req *types.QueryTraceTxRequest) (*typ
|
||||
return nil, status.Errorf(codes.InvalidArgument, "output limit cannot be negative, got %d", req.TraceConfig.Limit)
|
||||
}
|
||||
|
||||
// minus one to get the context of block beginning
|
||||
contextHeight := req.BlockNumber - 1
|
||||
if contextHeight < 1 {
|
||||
// 0 is a special value in `ContextWithHeight`
|
||||
contextHeight = 1
|
||||
}
|
||||
|
||||
ctx := sdk.UnwrapSDKContext(c)
|
||||
ctx = ctx.WithBlockHeight(req.BlockNumber)
|
||||
ctx = ctx.WithBlockHeight(contextHeight)
|
||||
ctx = ctx.WithBlockTime(req.BlockTime)
|
||||
ctx = ctx.WithHeaderHash(common.Hex2Bytes(req.BlockHash))
|
||||
|
||||
@@ -391,7 +400,10 @@ func (k Keeper) TraceTx(c context.Context, req *types.QueryTraceTxRequest) (*typ
|
||||
|
||||
tx := req.Msg.AsTransaction()
|
||||
txConfig.TxHash = tx.Hash()
|
||||
txConfig.TxIndex++
|
||||
if len(req.Predecessors) > 0 {
|
||||
txConfig.TxIndex++
|
||||
}
|
||||
|
||||
result, _, err := k.traceTx(ctx, cfg, txConfig, signer, tx, req.TraceConfig, false)
|
||||
if err != nil {
|
||||
// error will be returned with detail status from traceTx
|
||||
@@ -420,8 +432,15 @@ func (k Keeper) TraceBlock(c context.Context, req *types.QueryTraceBlockRequest)
|
||||
return nil, status.Errorf(codes.InvalidArgument, "output limit cannot be negative, got %d", req.TraceConfig.Limit)
|
||||
}
|
||||
|
||||
// minus one to get the context of block beginning
|
||||
contextHeight := req.BlockNumber - 1
|
||||
if contextHeight < 1 {
|
||||
// 0 is a special value in `ContextWithHeight`
|
||||
contextHeight = 1
|
||||
}
|
||||
|
||||
ctx := sdk.UnwrapSDKContext(c)
|
||||
ctx = ctx.WithBlockHeight(req.BlockNumber)
|
||||
ctx = ctx.WithBlockHeight(contextHeight)
|
||||
ctx = ctx.WithBlockTime(req.BlockTime)
|
||||
ctx = ctx.WithHeaderHash(common.Hex2Bytes(req.BlockHash))
|
||||
|
||||
@@ -471,101 +490,93 @@ func (k *Keeper) traceTx(
|
||||
) (*interface{}, uint, error) {
|
||||
// Assemble the structured logger or the JavaScript tracer
|
||||
var (
|
||||
tracer vm.EVMLogger
|
||||
tracer tracers.Tracer
|
||||
overrides *ethparams.ChainConfig
|
||||
err error
|
||||
timeout = defaultTraceTimeout
|
||||
)
|
||||
|
||||
msg, err := tx.AsMessage(signer, cfg.BaseFee)
|
||||
if err != nil {
|
||||
return nil, 0, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
|
||||
if traceConfig != nil && traceConfig.Overrides != nil {
|
||||
if traceConfig == nil {
|
||||
traceConfig = &types.TraceConfig{}
|
||||
}
|
||||
|
||||
if traceConfig.Overrides != nil {
|
||||
overrides = traceConfig.Overrides.EthereumConfig(cfg.ChainConfig.ChainID)
|
||||
}
|
||||
|
||||
switch {
|
||||
case traceConfig != nil && traceConfig.Tracer != "":
|
||||
timeout := defaultTraceTimeout
|
||||
// TODO: change timeout to time.duration
|
||||
// Used string to comply with go ethereum
|
||||
if traceConfig.Timeout != "" {
|
||||
timeout, err = time.ParseDuration(traceConfig.Timeout)
|
||||
if err != nil {
|
||||
return nil, 0, status.Errorf(codes.InvalidArgument, "timeout value: %s", err.Error())
|
||||
}
|
||||
}
|
||||
logConfig := logger.Config{
|
||||
EnableMemory: traceConfig.EnableMemory,
|
||||
DisableStorage: traceConfig.DisableStorage,
|
||||
DisableStack: traceConfig.DisableStack,
|
||||
EnableReturnData: traceConfig.EnableReturnData,
|
||||
Debug: traceConfig.Debug,
|
||||
Limit: int(traceConfig.Limit),
|
||||
Overrides: overrides,
|
||||
}
|
||||
|
||||
tCtx := &tracers.Context{
|
||||
BlockHash: txConfig.BlockHash,
|
||||
TxIndex: int(txConfig.TxIndex),
|
||||
TxHash: txConfig.TxHash,
|
||||
}
|
||||
tracer = logger.NewStructLogger(&logConfig)
|
||||
|
||||
// Construct the JavaScript tracer to execute with
|
||||
tCtx := &tracers.Context{
|
||||
BlockHash: txConfig.BlockHash,
|
||||
TxIndex: int(txConfig.TxIndex),
|
||||
TxHash: txConfig.TxHash,
|
||||
}
|
||||
|
||||
if traceConfig.Tracer != "" {
|
||||
if tracer, err = tracers.New(traceConfig.Tracer, tCtx); err != nil {
|
||||
return nil, 0, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
|
||||
// Handle timeouts and RPC cancellations
|
||||
deadlineCtx, cancel := context.WithTimeout(ctx.Context(), timeout)
|
||||
defer cancel()
|
||||
|
||||
go func() {
|
||||
<-deadlineCtx.Done()
|
||||
if errors.Is(deadlineCtx.Err(), context.DeadlineExceeded) {
|
||||
tracer.(tracers.Tracer).Stop(errors.New("execution timeout"))
|
||||
}
|
||||
}()
|
||||
|
||||
case traceConfig != nil:
|
||||
logConfig := logger.Config{
|
||||
EnableMemory: traceConfig.EnableMemory,
|
||||
DisableStorage: traceConfig.DisableStorage,
|
||||
DisableStack: traceConfig.DisableStack,
|
||||
EnableReturnData: traceConfig.EnableReturnData,
|
||||
Debug: traceConfig.Debug,
|
||||
Limit: int(traceConfig.Limit),
|
||||
Overrides: overrides,
|
||||
}
|
||||
tracer = logger.NewStructLogger(&logConfig)
|
||||
default:
|
||||
tracer = types.NewTracer(types.TracerStruct, msg, cfg.ChainConfig, ctx.BlockHeight())
|
||||
}
|
||||
|
||||
// Define a meaningful timeout of a single transaction trace
|
||||
if traceConfig.Timeout != "" {
|
||||
if timeout, err = time.ParseDuration(traceConfig.Timeout); err != nil {
|
||||
return nil, 0, status.Errorf(codes.InvalidArgument, "timeout value: %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// Handle timeouts and RPC cancellations
|
||||
deadlineCtx, cancel := context.WithTimeout(ctx.Context(), timeout)
|
||||
defer cancel()
|
||||
|
||||
go func() {
|
||||
<-deadlineCtx.Done()
|
||||
if errors.Is(deadlineCtx.Err(), context.DeadlineExceeded) {
|
||||
tracer.Stop(errors.New("execution timeout"))
|
||||
}
|
||||
}()
|
||||
|
||||
res, err := k.ApplyMessageWithConfig(ctx, msg, tracer, commitMessage, cfg, txConfig)
|
||||
if err != nil {
|
||||
return nil, 0, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
|
||||
var result interface{}
|
||||
|
||||
// Depending on the tracer type, format and return the trace result data.
|
||||
switch tracer := tracer.(type) {
|
||||
case *logger.StructLogger:
|
||||
returnVal := ""
|
||||
revert := res.Revert()
|
||||
if len(revert) > 0 {
|
||||
returnVal = fmt.Sprintf("%x", revert)
|
||||
} else {
|
||||
returnVal = fmt.Sprintf("%x", res.Return())
|
||||
}
|
||||
result = types.ExecutionResult{
|
||||
Gas: res.GasUsed,
|
||||
Failed: res.Failed(),
|
||||
ReturnValue: returnVal,
|
||||
StructLogs: types.FormatLogs(tracer.StructLogs()),
|
||||
}
|
||||
case tracers.Tracer:
|
||||
result, err = tracer.GetResult()
|
||||
if err != nil {
|
||||
return nil, 0, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, 0, status.Errorf(codes.InvalidArgument, "invalid tracer type %T", tracer)
|
||||
result, err = tracer.GetResult()
|
||||
if err != nil {
|
||||
return nil, 0, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
|
||||
return &result, txConfig.LogIndex + uint(len(res.Logs)), nil
|
||||
}
|
||||
|
||||
// BaseFee implements the Query/BaseFee gRPC method
|
||||
func (k Keeper) BaseFee(c context.Context, _ *types.QueryBaseFeeRequest) (*types.QueryBaseFeeResponse, error) {
|
||||
ctx := sdk.UnwrapSDKContext(c)
|
||||
|
||||
params := k.GetParams(ctx)
|
||||
ethCfg := params.ChainConfig.EthereumConfig(k.eip155ChainID)
|
||||
baseFee := k.GetBaseFee(ctx, ethCfg)
|
||||
|
||||
res := &types.QueryBaseFeeResponse{}
|
||||
if baseFee != nil {
|
||||
aux := sdkmath.NewIntFromBigInt(baseFee)
|
||||
res.BaseFee = &aux
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
+115
-42
@@ -5,12 +5,15 @@ import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"cosmossdk.io/math"
|
||||
sdkmath "cosmossdk.io/math"
|
||||
|
||||
"github.com/cerc-io/laconicd/x/evm/statedb"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
ethlogger "github.com/ethereum/go-ethereum/eth/tracers/logger"
|
||||
ethparams "github.com/ethereum/go-ethereum/params"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
|
||||
@@ -523,7 +526,7 @@ func (suite *KeeperTestSuite) TestEstimateGas() {
|
||||
}, false, 0, false},
|
||||
// estimate gas of an erc20 contract deployment, the exact gas number is checked with geth
|
||||
{"contract deployment", func() {
|
||||
ctorArgs, err := types.ERC20Contract.ABI.Pack("", &suite.address, math.NewIntWithDecimal(1000, 18).BigInt())
|
||||
ctorArgs, err := types.ERC20Contract.ABI.Pack("", &suite.address, sdkmath.NewIntWithDecimal(1000, 18).BigInt())
|
||||
suite.Require().NoError(err)
|
||||
data := append(types.ERC20Contract.Bin, ctorArgs...)
|
||||
args = types.TransactionArgs{
|
||||
@@ -533,7 +536,7 @@ func (suite *KeeperTestSuite) TestEstimateGas() {
|
||||
}, true, 1186778, false},
|
||||
// estimate gas of an erc20 transfer, the exact gas number is checked with geth
|
||||
{"erc20 transfer", func() {
|
||||
contractAddr := suite.DeployTestContract(suite.T(), suite.address, math.NewIntWithDecimal(1000, 18).BigInt())
|
||||
contractAddr := suite.DeployTestContract(suite.T(), suite.address, sdkmath.NewIntWithDecimal(1000, 18).BigInt())
|
||||
suite.Commit()
|
||||
transferData, err := types.ERC20Contract.ABI.Pack("transfer", common.HexToAddress("0x378c50D9264C63F3F92B806d4ee56E9D86FfB3Ec"), big.NewInt(1000))
|
||||
suite.Require().NoError(err)
|
||||
@@ -558,7 +561,7 @@ func (suite *KeeperTestSuite) TestEstimateGas() {
|
||||
gasCap = 20000
|
||||
}, false, 0, true},
|
||||
{"contract deployment w/ enableFeemarket", func() {
|
||||
ctorArgs, err := types.ERC20Contract.ABI.Pack("", &suite.address, math.NewIntWithDecimal(1000, 18).BigInt())
|
||||
ctorArgs, err := types.ERC20Contract.ABI.Pack("", &suite.address, sdkmath.NewIntWithDecimal(1000, 18).BigInt())
|
||||
suite.Require().NoError(err)
|
||||
data := append(types.ERC20Contract.Bin, ctorArgs...)
|
||||
args = types.TransactionArgs{
|
||||
@@ -567,7 +570,7 @@ func (suite *KeeperTestSuite) TestEstimateGas() {
|
||||
}
|
||||
}, true, 1186778, true},
|
||||
{"erc20 transfer w/ enableFeemarket", func() {
|
||||
contractAddr := suite.DeployTestContract(suite.T(), suite.address, math.NewIntWithDecimal(1000, 18).BigInt())
|
||||
contractAddr := suite.DeployTestContract(suite.T(), suite.address, sdkmath.NewIntWithDecimal(1000, 18).BigInt())
|
||||
suite.Commit()
|
||||
transferData, err := types.ERC20Contract.ABI.Pack("transfer", common.HexToAddress("0x378c50D9264C63F3F92B806d4ee56E9D86FfB3Ec"), big.NewInt(1000))
|
||||
suite.Require().NoError(err)
|
||||
@@ -606,7 +609,6 @@ func (suite *KeeperTestSuite) TestTraceTx() {
|
||||
var (
|
||||
txMsg *types.MsgEthereumTx
|
||||
traceConfig *types.TraceConfig
|
||||
txIndex uint64
|
||||
predecessors []*types.MsgEthereumTx
|
||||
)
|
||||
|
||||
@@ -614,23 +616,21 @@ func (suite *KeeperTestSuite) TestTraceTx() {
|
||||
msg string
|
||||
malleate func()
|
||||
expPass bool
|
||||
traceResponse []byte
|
||||
traceResponse string
|
||||
enableFeemarket bool
|
||||
}{
|
||||
{
|
||||
msg: "default trace",
|
||||
malleate: func() {
|
||||
txIndex = 0
|
||||
traceConfig = nil
|
||||
predecessors = []*types.MsgEthereumTx{}
|
||||
},
|
||||
expPass: true,
|
||||
traceResponse: []byte{0x7b, 0x22, 0x67, 0x61, 0x73, 0x22, 0x3a, 0x33, 0x34, 0x38, 0x32, 0x38, 0x2c, 0x22, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x3a, 0x22, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x22, 0x2c, 0x22, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x4c, 0x6f, 0x67, 0x73, 0x22, 0x3a, 0x5b, 0x7b, 0x22, 0x70, 0x63, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x6f, 0x70, 0x22, 0x3a, 0x22, 0x50, 0x55, 0x53, 0x48, 0x31, 0x22, 0x2c, 0x22, 0x67, 0x61, 0x73, 0x22, 0x3a},
|
||||
traceResponse: "{\"gas\":34828,\"failed\":false,\"returnValue\":\"0000000000000000000000000000000000000000000000000000000000000001\",\"structLogs\":[{\"pc\":0,\"op\":\"PUSH1\",\"gas\":",
|
||||
},
|
||||
{
|
||||
msg: "default trace with filtered response",
|
||||
malleate: func() {
|
||||
txIndex = 0
|
||||
traceConfig = &types.TraceConfig{
|
||||
DisableStack: true,
|
||||
DisableStorage: true,
|
||||
@@ -639,25 +639,23 @@ func (suite *KeeperTestSuite) TestTraceTx() {
|
||||
predecessors = []*types.MsgEthereumTx{}
|
||||
},
|
||||
expPass: true,
|
||||
traceResponse: []byte{0x7b, 0x22, 0x67, 0x61, 0x73, 0x22, 0x3a, 0x33, 0x34, 0x38, 0x32, 0x38, 0x2c, 0x22, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x3a, 0x22, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x22, 0x2c, 0x22, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x4c, 0x6f, 0x67, 0x73, 0x22, 0x3a, 0x5b, 0x7b, 0x22, 0x70, 0x63, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x6f, 0x70, 0x22, 0x3a, 0x22, 0x50, 0x55, 0x53, 0x48, 0x31, 0x22, 0x2c, 0x22, 0x67, 0x61, 0x73, 0x22, 0x3a},
|
||||
traceResponse: "{\"gas\":34828,\"failed\":false,\"returnValue\":\"0000000000000000000000000000000000000000000000000000000000000001\",\"structLogs\":[{\"pc\":0,\"op\":\"PUSH1\",\"gas\":",
|
||||
enableFeemarket: false,
|
||||
},
|
||||
{
|
||||
msg: "javascript tracer",
|
||||
malleate: func() {
|
||||
txIndex = 0
|
||||
traceConfig = &types.TraceConfig{
|
||||
Tracer: "{data: [], fault: function(log) {}, step: function(log) { if(log.op.toString() == \"CALL\") this.data.push(log.stack.peek(0)); }, result: function() { return this.data; }}",
|
||||
}
|
||||
predecessors = []*types.MsgEthereumTx{}
|
||||
},
|
||||
expPass: true,
|
||||
traceResponse: []byte{0x5b, 0x5d},
|
||||
traceResponse: "[]",
|
||||
},
|
||||
{
|
||||
msg: "default trace with enableFeemarket",
|
||||
malleate: func() {
|
||||
txIndex = 0
|
||||
traceConfig = &types.TraceConfig{
|
||||
DisableStack: true,
|
||||
DisableStorage: true,
|
||||
@@ -666,26 +664,24 @@ func (suite *KeeperTestSuite) TestTraceTx() {
|
||||
predecessors = []*types.MsgEthereumTx{}
|
||||
},
|
||||
expPass: true,
|
||||
traceResponse: []byte{0x7b, 0x22, 0x67, 0x61, 0x73, 0x22, 0x3a, 0x33, 0x34, 0x38, 0x32, 0x38, 0x2c, 0x22, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x3a, 0x22, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x22, 0x2c, 0x22, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x4c, 0x6f, 0x67, 0x73, 0x22, 0x3a, 0x5b, 0x7b, 0x22, 0x70, 0x63, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x6f, 0x70, 0x22, 0x3a, 0x22, 0x50, 0x55, 0x53, 0x48, 0x31, 0x22, 0x2c, 0x22, 0x67, 0x61, 0x73, 0x22, 0x3a},
|
||||
traceResponse: "{\"gas\":34828,\"failed\":false,\"returnValue\":\"0000000000000000000000000000000000000000000000000000000000000001\",\"structLogs\":[{\"pc\":0,\"op\":\"PUSH1\",\"gas\":",
|
||||
enableFeemarket: true,
|
||||
},
|
||||
{
|
||||
msg: "javascript tracer with enableFeemarket",
|
||||
malleate: func() {
|
||||
txIndex = 0
|
||||
traceConfig = &types.TraceConfig{
|
||||
Tracer: "{data: [], fault: function(log) {}, step: function(log) { if(log.op.toString() == \"CALL\") this.data.push(log.stack.peek(0)); }, result: function() { return this.data; }}",
|
||||
}
|
||||
predecessors = []*types.MsgEthereumTx{}
|
||||
},
|
||||
expPass: true,
|
||||
traceResponse: []byte{0x5b, 0x5d},
|
||||
traceResponse: "[]",
|
||||
enableFeemarket: true,
|
||||
},
|
||||
{
|
||||
msg: "default tracer with predecessors",
|
||||
malleate: func() {
|
||||
txIndex = 1
|
||||
traceConfig = nil
|
||||
|
||||
// increase nonce to avoid address collision
|
||||
@@ -693,17 +689,17 @@ func (suite *KeeperTestSuite) TestTraceTx() {
|
||||
vmdb.SetNonce(suite.address, vmdb.GetNonce(suite.address)+1)
|
||||
suite.Require().NoError(vmdb.Commit())
|
||||
|
||||
contractAddr := suite.DeployTestContract(suite.T(), suite.address, math.NewIntWithDecimal(1000, 18).BigInt())
|
||||
contractAddr := suite.DeployTestContract(suite.T(), suite.address, sdkmath.NewIntWithDecimal(1000, 18).BigInt())
|
||||
suite.Commit()
|
||||
// Generate token transfer transaction
|
||||
firstTx := suite.TransferERC20Token(suite.T(), contractAddr, suite.address, common.HexToAddress("0x378c50D9264C63F3F92B806d4ee56E9D86FfB3Ec"), math.NewIntWithDecimal(1, 18).BigInt())
|
||||
txMsg = suite.TransferERC20Token(suite.T(), contractAddr, suite.address, common.HexToAddress("0x378c50D9264C63F3F92B806d4ee56E9D86FfB3Ec"), math.NewIntWithDecimal(1, 18).BigInt())
|
||||
firstTx := suite.TransferERC20Token(suite.T(), contractAddr, suite.address, common.HexToAddress("0x378c50D9264C63F3F92B806d4ee56E9D86FfB3Ec"), sdkmath.NewIntWithDecimal(1, 18).BigInt())
|
||||
txMsg = suite.TransferERC20Token(suite.T(), contractAddr, suite.address, common.HexToAddress("0x378c50D9264C63F3F92B806d4ee56E9D86FfB3Ec"), sdkmath.NewIntWithDecimal(1, 18).BigInt())
|
||||
suite.Commit()
|
||||
|
||||
predecessors = append(predecessors, firstTx)
|
||||
},
|
||||
expPass: true,
|
||||
traceResponse: []byte{0x7b, 0x22, 0x67, 0x61, 0x73, 0x22, 0x3a, 0x33, 0x34, 0x38, 0x32, 0x38, 0x2c, 0x22, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x3a, 0x22, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x22, 0x2c, 0x22, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x4c, 0x6f, 0x67, 0x73, 0x22, 0x3a, 0x5b, 0x7b, 0x22, 0x70, 0x63, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x6f, 0x70, 0x22, 0x3a, 0x22, 0x50, 0x55, 0x53, 0x48, 0x31, 0x22, 0x2c, 0x22, 0x67, 0x61, 0x73, 0x22, 0x3a},
|
||||
traceResponse: "{\"gas\":34828,\"failed\":false,\"returnValue\":\"0000000000000000000000000000000000000000000000000000000000000001\",\"structLogs\":[{\"pc\":0,\"op\":\"PUSH1\",\"gas\":",
|
||||
enableFeemarket: false,
|
||||
},
|
||||
}
|
||||
@@ -713,17 +709,16 @@ func (suite *KeeperTestSuite) TestTraceTx() {
|
||||
suite.enableFeemarket = tc.enableFeemarket
|
||||
suite.SetupTest()
|
||||
// Deploy contract
|
||||
contractAddr := suite.DeployTestContract(suite.T(), suite.address, math.NewIntWithDecimal(1000, 18).BigInt())
|
||||
contractAddr := suite.DeployTestContract(suite.T(), suite.address, sdkmath.NewIntWithDecimal(1000, 18).BigInt())
|
||||
suite.Commit()
|
||||
// Generate token transfer transaction
|
||||
txMsg = suite.TransferERC20Token(suite.T(), contractAddr, suite.address, common.HexToAddress("0x378c50D9264C63F3F92B806d4ee56E9D86FfB3Ec"), math.NewIntWithDecimal(1, 18).BigInt())
|
||||
txMsg = suite.TransferERC20Token(suite.T(), contractAddr, suite.address, common.HexToAddress("0x378c50D9264C63F3F92B806d4ee56E9D86FfB3Ec"), sdkmath.NewIntWithDecimal(1, 18).BigInt())
|
||||
suite.Commit()
|
||||
|
||||
tc.malleate()
|
||||
traceReq := types.QueryTraceTxRequest{
|
||||
Msg: txMsg,
|
||||
TraceConfig: traceConfig,
|
||||
TxIndex: txIndex,
|
||||
Predecessors: predecessors,
|
||||
}
|
||||
res, err := suite.queryClient.TraceTx(sdk.WrapSDKContext(suite.ctx), &traceReq)
|
||||
@@ -733,9 +728,14 @@ func (suite *KeeperTestSuite) TestTraceTx() {
|
||||
suite.Require().NoError(err)
|
||||
// if data is to big, slice the result
|
||||
if len(res.Data) > 150 {
|
||||
suite.Require().Equal(tc.traceResponse, res.Data[:150])
|
||||
suite.Require().Equal(tc.traceResponse, string(res.Data[:150]))
|
||||
} else {
|
||||
suite.Require().Equal(tc.traceResponse, res.Data)
|
||||
suite.Require().Equal(tc.traceResponse, string(res.Data))
|
||||
}
|
||||
if traceConfig == nil || traceConfig.Tracer == "" {
|
||||
var result ethlogger.ExecutionResult
|
||||
suite.Require().NoError(json.Unmarshal(res.Data, &result))
|
||||
suite.Require().Positive(result.Gas)
|
||||
}
|
||||
} else {
|
||||
suite.Require().Error(err)
|
||||
@@ -756,7 +756,7 @@ func (suite *KeeperTestSuite) TestTraceBlock() {
|
||||
msg string
|
||||
malleate func()
|
||||
expPass bool
|
||||
traceResponse []byte
|
||||
traceResponse string
|
||||
enableFeemarket bool
|
||||
}{
|
||||
{
|
||||
@@ -765,7 +765,7 @@ func (suite *KeeperTestSuite) TestTraceBlock() {
|
||||
traceConfig = nil
|
||||
},
|
||||
expPass: true,
|
||||
traceResponse: []byte{0x5b, 0x7b, 0x22, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x3a, 0x7b, 0x22, 0x67, 0x61, 0x73, 0x22, 0x3a, 0x33, 0x34, 0x38, 0x32, 0x38, 0x2c, 0x22, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x3a, 0x22, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x22, 0x2c, 0x22, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x4c, 0x6f, 0x67, 0x73, 0x22, 0x3a, 0x5b, 0x7b, 0x22, 0x70, 0x63, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x6f, 0x70, 0x22, 0x3a, 0x22, 0x50, 0x55},
|
||||
traceResponse: "[{\"result\":{\"gas\":34828,\"failed\":false,\"returnValue\":\"0000000000000000000000000000000000000000000000000000000000000001\",\"structLogs\":[{\"pc\":0,\"op\":\"PU",
|
||||
},
|
||||
{
|
||||
msg: "filtered trace",
|
||||
@@ -777,7 +777,7 @@ func (suite *KeeperTestSuite) TestTraceBlock() {
|
||||
}
|
||||
},
|
||||
expPass: true,
|
||||
traceResponse: []byte{0x5b, 0x7b, 0x22, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x3a, 0x7b, 0x22, 0x67, 0x61, 0x73, 0x22, 0x3a, 0x33, 0x34, 0x38, 0x32, 0x38, 0x2c, 0x22, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x3a, 0x22, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x22, 0x2c, 0x22, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x4c, 0x6f, 0x67, 0x73, 0x22, 0x3a, 0x5b, 0x7b, 0x22, 0x70, 0x63, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x6f, 0x70, 0x22, 0x3a, 0x22, 0x50, 0x55},
|
||||
traceResponse: "[{\"result\":{\"gas\":34828,\"failed\":false,\"returnValue\":\"0000000000000000000000000000000000000000000000000000000000000001\",\"structLogs\":[{\"pc\":0,\"op\":\"PU",
|
||||
},
|
||||
{
|
||||
msg: "javascript tracer",
|
||||
@@ -787,7 +787,7 @@ func (suite *KeeperTestSuite) TestTraceBlock() {
|
||||
}
|
||||
},
|
||||
expPass: true,
|
||||
traceResponse: []byte{0x5b, 0x7b, 0x22, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x3a, 0x5b, 0x5d, 0x7d, 0x5d},
|
||||
traceResponse: "[{\"result\":[]}]",
|
||||
},
|
||||
{
|
||||
msg: "default trace with enableFeemarket and filtered return",
|
||||
@@ -799,7 +799,7 @@ func (suite *KeeperTestSuite) TestTraceBlock() {
|
||||
}
|
||||
},
|
||||
expPass: true,
|
||||
traceResponse: []byte{0x5b, 0x7b, 0x22, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x3a, 0x7b, 0x22, 0x67, 0x61, 0x73, 0x22, 0x3a, 0x33, 0x34, 0x38, 0x32, 0x38, 0x2c, 0x22, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x3a, 0x22, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x22, 0x2c, 0x22, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x4c, 0x6f, 0x67, 0x73, 0x22, 0x3a, 0x5b, 0x7b, 0x22, 0x70, 0x63, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x6f, 0x70, 0x22, 0x3a, 0x22, 0x50, 0x55},
|
||||
traceResponse: "[{\"result\":{\"gas\":34828,\"failed\":false,\"returnValue\":\"0000000000000000000000000000000000000000000000000000000000000001\",\"structLogs\":[{\"pc\":0,\"op\":\"PU",
|
||||
enableFeemarket: true,
|
||||
},
|
||||
{
|
||||
@@ -810,7 +810,7 @@ func (suite *KeeperTestSuite) TestTraceBlock() {
|
||||
}
|
||||
},
|
||||
expPass: true,
|
||||
traceResponse: []byte{0x5b, 0x7b, 0x22, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x3a, 0x5b, 0x5d, 0x7d, 0x5d},
|
||||
traceResponse: "[{\"result\":[]}]",
|
||||
enableFeemarket: true,
|
||||
},
|
||||
{
|
||||
@@ -823,17 +823,17 @@ func (suite *KeeperTestSuite) TestTraceBlock() {
|
||||
vmdb.SetNonce(suite.address, vmdb.GetNonce(suite.address)+1)
|
||||
suite.Require().NoError(vmdb.Commit())
|
||||
|
||||
contractAddr := suite.DeployTestContract(suite.T(), suite.address, math.NewIntWithDecimal(1000, 18).BigInt())
|
||||
contractAddr := suite.DeployTestContract(suite.T(), suite.address, sdkmath.NewIntWithDecimal(1000, 18).BigInt())
|
||||
suite.Commit()
|
||||
// create multiple transactions in the same block
|
||||
firstTx := suite.TransferERC20Token(suite.T(), contractAddr, suite.address, common.HexToAddress("0x378c50D9264C63F3F92B806d4ee56E9D86FfB3Ec"), math.NewIntWithDecimal(1, 18).BigInt())
|
||||
secondTx := suite.TransferERC20Token(suite.T(), contractAddr, suite.address, common.HexToAddress("0x378c50D9264C63F3F92B806d4ee56E9D86FfB3Ec"), math.NewIntWithDecimal(1, 18).BigInt())
|
||||
firstTx := suite.TransferERC20Token(suite.T(), contractAddr, suite.address, common.HexToAddress("0x378c50D9264C63F3F92B806d4ee56E9D86FfB3Ec"), sdkmath.NewIntWithDecimal(1, 18).BigInt())
|
||||
secondTx := suite.TransferERC20Token(suite.T(), contractAddr, suite.address, common.HexToAddress("0x378c50D9264C63F3F92B806d4ee56E9D86FfB3Ec"), sdkmath.NewIntWithDecimal(1, 18).BigInt())
|
||||
suite.Commit()
|
||||
// overwrite txs to include only the ones on new block
|
||||
txs = append([]*types.MsgEthereumTx{}, firstTx, secondTx)
|
||||
},
|
||||
expPass: true,
|
||||
traceResponse: []byte{0x5b, 0x7b, 0x22, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x3a, 0x7b, 0x22, 0x67, 0x61, 0x73, 0x22, 0x3a, 0x33, 0x34, 0x38, 0x32, 0x38, 0x2c, 0x22, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x3a, 0x22, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x22, 0x2c, 0x22, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x4c, 0x6f, 0x67, 0x73, 0x22, 0x3a, 0x5b, 0x7b, 0x22, 0x70, 0x63, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x6f, 0x70, 0x22, 0x3a, 0x22, 0x50, 0x55},
|
||||
traceResponse: "[{\"result\":{\"gas\":34828,\"failed\":false,\"returnValue\":\"0000000000000000000000000000000000000000000000000000000000000001\",\"structLogs\":[{\"pc\":0,\"op\":\"PU",
|
||||
enableFeemarket: false,
|
||||
},
|
||||
}
|
||||
@@ -844,10 +844,10 @@ func (suite *KeeperTestSuite) TestTraceBlock() {
|
||||
suite.enableFeemarket = tc.enableFeemarket
|
||||
suite.SetupTest()
|
||||
// Deploy contract
|
||||
contractAddr := suite.DeployTestContract(suite.T(), suite.address, math.NewIntWithDecimal(1000, 18).BigInt())
|
||||
contractAddr := suite.DeployTestContract(suite.T(), suite.address, sdkmath.NewIntWithDecimal(1000, 18).BigInt())
|
||||
suite.Commit()
|
||||
// Generate token transfer transaction
|
||||
txMsg := suite.TransferERC20Token(suite.T(), contractAddr, suite.address, common.HexToAddress("0x378c50D9264C63F3F92B806d4ee56E9D86FfB3Ec"), math.NewIntWithDecimal(1, 18).BigInt())
|
||||
txMsg := suite.TransferERC20Token(suite.T(), contractAddr, suite.address, common.HexToAddress("0x378c50D9264C63F3F92B806d4ee56E9D86FfB3Ec"), sdkmath.NewIntWithDecimal(1, 18).BigInt())
|
||||
suite.Commit()
|
||||
|
||||
txs = append(txs, txMsg)
|
||||
@@ -863,9 +863,9 @@ func (suite *KeeperTestSuite) TestTraceBlock() {
|
||||
suite.Require().NoError(err)
|
||||
// if data is to big, slice the result
|
||||
if len(res.Data) > 150 {
|
||||
suite.Require().Equal(tc.traceResponse, res.Data[:150])
|
||||
suite.Require().Equal(tc.traceResponse, string(res.Data[:150]))
|
||||
} else {
|
||||
suite.Require().Equal(tc.traceResponse, res.Data)
|
||||
suite.Require().Equal(tc.traceResponse, string(res.Data))
|
||||
}
|
||||
} else {
|
||||
suite.Require().Error(err)
|
||||
@@ -882,7 +882,7 @@ func (suite *KeeperTestSuite) TestNonceInQuery() {
|
||||
suite.Require().NoError(err)
|
||||
address := common.BytesToAddress(priv.PubKey().Address().Bytes())
|
||||
suite.Require().Equal(uint64(0), suite.app.EvmKeeper.GetNonce(suite.ctx, address))
|
||||
supply := math.NewIntWithDecimal(1000, 18).BigInt()
|
||||
supply := sdkmath.NewIntWithDecimal(1000, 18).BigInt()
|
||||
|
||||
// accupy nonce 0
|
||||
_ = suite.DeployTestContract(suite.T(), address, supply)
|
||||
@@ -908,3 +908,76 @@ func (suite *KeeperTestSuite) TestNonceInQuery() {
|
||||
})
|
||||
suite.Require().NoError(err)
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) TestQueryBaseFee() {
|
||||
var (
|
||||
aux sdkmath.Int
|
||||
expRes *types.QueryBaseFeeResponse
|
||||
)
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
malleate func()
|
||||
expPass bool
|
||||
enableFeemarket bool
|
||||
enableLondonHF bool
|
||||
}{
|
||||
{
|
||||
"pass - default Base Fee",
|
||||
func() {
|
||||
initialBaseFee := sdkmath.NewInt(ethparams.InitialBaseFee)
|
||||
expRes = &types.QueryBaseFeeResponse{BaseFee: &initialBaseFee}
|
||||
},
|
||||
true, true, true,
|
||||
},
|
||||
{
|
||||
"pass - non-nil Base Fee",
|
||||
func() {
|
||||
baseFee := sdk.OneInt().BigInt()
|
||||
suite.app.FeeMarketKeeper.SetBaseFee(suite.ctx, baseFee)
|
||||
|
||||
aux = sdkmath.NewIntFromBigInt(baseFee)
|
||||
expRes = &types.QueryBaseFeeResponse{BaseFee: &aux}
|
||||
},
|
||||
true, true, true,
|
||||
},
|
||||
{
|
||||
"pass - nil Base Fee when london hardfork not activated",
|
||||
func() {
|
||||
baseFee := sdk.OneInt().BigInt()
|
||||
suite.app.FeeMarketKeeper.SetBaseFee(suite.ctx, baseFee)
|
||||
|
||||
expRes = &types.QueryBaseFeeResponse{}
|
||||
},
|
||||
true, true, false,
|
||||
},
|
||||
{
|
||||
"pass - zero Base Fee when feemarket not activated",
|
||||
func() {
|
||||
baseFee := sdk.ZeroInt()
|
||||
expRes = &types.QueryBaseFeeResponse{BaseFee: &baseFee}
|
||||
},
|
||||
true, false, true,
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
suite.Run(tc.name, func() {
|
||||
suite.enableFeemarket = tc.enableFeemarket
|
||||
suite.enableLondonHF = tc.enableLondonHF
|
||||
suite.SetupTest()
|
||||
|
||||
tc.malleate()
|
||||
|
||||
res, err := suite.queryClient.BaseFee(suite.ctx.Context(), &types.QueryBaseFeeRequest{})
|
||||
if tc.expPass {
|
||||
suite.Require().NotNil(res)
|
||||
suite.Require().Equal(expRes, res, tc.name)
|
||||
suite.Require().NoError(err)
|
||||
} else {
|
||||
suite.Require().Error(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
suite.enableFeemarket = false
|
||||
suite.enableLondonHF = true
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"github.com/cerc-io/laconicd/x/evm/types"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||
)
|
||||
|
||||
@@ -19,9 +19,9 @@ func NewMultiEvmHooks(hooks ...types.EvmHooks) MultiEvmHooks {
|
||||
}
|
||||
|
||||
// PostTxProcessing delegate the call to underlying hooks
|
||||
func (mh MultiEvmHooks) PostTxProcessing(ctx sdk.Context, from common.Address, to *common.Address, receipt *ethtypes.Receipt) error {
|
||||
func (mh MultiEvmHooks) PostTxProcessing(ctx sdk.Context, msg core.Message, receipt *ethtypes.Receipt) error {
|
||||
for i := range mh {
|
||||
if err := mh[i].PostTxProcessing(ctx, from, to, receipt); err != nil {
|
||||
if err := mh[i].PostTxProcessing(ctx, msg, receipt); err != nil {
|
||||
return sdkerrors.Wrapf(err, "EVM hook %T failed", mh[i])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||
|
||||
"github.com/cerc-io/laconicd/x/evm/keeper"
|
||||
@@ -18,7 +19,7 @@ type LogRecordHook struct {
|
||||
Logs []*ethtypes.Log
|
||||
}
|
||||
|
||||
func (dh *LogRecordHook) PostTxProcessing(ctx sdk.Context, from common.Address, to *common.Address, receipt *ethtypes.Receipt) error {
|
||||
func (dh *LogRecordHook) PostTxProcessing(ctx sdk.Context, msg core.Message, receipt *ethtypes.Receipt) error {
|
||||
dh.Logs = receipt.Logs
|
||||
return nil
|
||||
}
|
||||
@@ -26,7 +27,7 @@ func (dh *LogRecordHook) PostTxProcessing(ctx sdk.Context, from common.Address,
|
||||
// FailureHook always fail
|
||||
type FailureHook struct{}
|
||||
|
||||
func (dh FailureHook) PostTxProcessing(ctx sdk.Context, from common.Address, to *common.Address, receipt *ethtypes.Receipt) error {
|
||||
func (dh FailureHook) PostTxProcessing(ctx sdk.Context, msg core.Message, receipt *ethtypes.Receipt) error {
|
||||
return errors.New("post tx processing failed")
|
||||
}
|
||||
|
||||
@@ -81,7 +82,7 @@ func (suite *KeeperTestSuite) TestEvmHooks() {
|
||||
TxHash: txHash,
|
||||
Logs: logs,
|
||||
}
|
||||
result := k.PostTxProcessing(ctx, common.Address{}, nil, receipt)
|
||||
result := k.PostTxProcessing(ctx, ethtypes.Message{}, receipt)
|
||||
|
||||
tc.expFunc(hook, result)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,290 @@
|
||||
package keeper_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"math/big"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"github.com/cerc-io/laconicd/app"
|
||||
"github.com/cerc-io/laconicd/crypto/ethsecp256k1"
|
||||
"github.com/cerc-io/laconicd/encoding"
|
||||
"github.com/cerc-io/laconicd/tests"
|
||||
"github.com/cerc-io/laconicd/testutil"
|
||||
"github.com/cerc-io/laconicd/x/feemarket/types"
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
authtx "github.com/cosmos/cosmos-sdk/x/auth/tx"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||
|
||||
evmtypes "github.com/cerc-io/laconicd/x/evm/types"
|
||||
"github.com/cosmos/cosmos-sdk/simapp"
|
||||
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
dbm "github.com/tendermint/tm-db"
|
||||
)
|
||||
|
||||
var _ = Describe("Feemarket", func() {
|
||||
var privKey *ethsecp256k1.PrivKey
|
||||
|
||||
Describe("Performing EVM transactions", func() {
|
||||
type txParams struct {
|
||||
gasLimit uint64
|
||||
gasPrice *big.Int
|
||||
gasFeeCap *big.Int
|
||||
gasTipCap *big.Int
|
||||
accesses *ethtypes.AccessList
|
||||
}
|
||||
type getprices func() txParams
|
||||
|
||||
Context("with MinGasPrices (feemarket param) < BaseFee (feemarket)", func() {
|
||||
var (
|
||||
baseFee int64
|
||||
minGasPrices int64
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
baseFee = 10_000_000_000
|
||||
minGasPrices = baseFee - 5_000_000_000
|
||||
|
||||
// Note that the tests run the same transactions with `gasLimit =
|
||||
// 100_000`. With the fee calculation `Fee = (baseFee + tip) * gasLimit`,
|
||||
// a `minGasPrices = 5_000_000_000` results in `minGlobalFee =
|
||||
// 500_000_000_000_000`
|
||||
privKey, _ = setupTestWithContext("1", sdk.NewDec(minGasPrices), sdk.NewInt(baseFee))
|
||||
})
|
||||
|
||||
Context("during CheckTx", func() {
|
||||
DescribeTable("should accept transactions with gas Limit > 0",
|
||||
func(malleate getprices) {
|
||||
p := malleate()
|
||||
to := tests.GenerateAddress()
|
||||
msgEthereumTx := buildEthTx(privKey, &to, p.gasLimit, p.gasPrice, p.gasFeeCap, p.gasTipCap, p.accesses)
|
||||
res := checkEthTx(privKey, msgEthereumTx)
|
||||
Expect(res.IsOK()).To(Equal(true), "transaction should have succeeded", res.GetLog())
|
||||
},
|
||||
Entry("legacy tx", func() txParams {
|
||||
return txParams{100000, big.NewInt(baseFee), nil, nil, nil}
|
||||
}),
|
||||
Entry("dynamic tx", func() txParams {
|
||||
return txParams{100000, nil, big.NewInt(baseFee), big.NewInt(0), ðtypes.AccessList{}}
|
||||
}),
|
||||
)
|
||||
DescribeTable("should not accept transactions with gas Limit > 0",
|
||||
func(malleate getprices) {
|
||||
p := malleate()
|
||||
to := tests.GenerateAddress()
|
||||
msgEthereumTx := buildEthTx(privKey, &to, p.gasLimit, p.gasPrice, p.gasFeeCap, p.gasTipCap, p.accesses)
|
||||
res := checkEthTx(privKey, msgEthereumTx)
|
||||
Expect(res.IsOK()).To(Equal(false), "transaction should have succeeded", res.GetLog())
|
||||
},
|
||||
Entry("legacy tx", func() txParams {
|
||||
return txParams{0, big.NewInt(baseFee), nil, nil, nil}
|
||||
}),
|
||||
Entry("dynamic tx", func() txParams {
|
||||
return txParams{0, nil, big.NewInt(baseFee), big.NewInt(0), ðtypes.AccessList{}}
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
Context("during DeliverTx", func() {
|
||||
DescribeTable("should accept transactions with gas Limit > 0",
|
||||
func(malleate getprices) {
|
||||
p := malleate()
|
||||
to := tests.GenerateAddress()
|
||||
msgEthereumTx := buildEthTx(privKey, &to, p.gasLimit, p.gasPrice, p.gasFeeCap, p.gasTipCap, p.accesses)
|
||||
res := deliverEthTx(privKey, msgEthereumTx)
|
||||
Expect(res.IsOK()).To(Equal(true), "transaction should have succeeded", res.GetLog())
|
||||
},
|
||||
Entry("legacy tx", func() txParams {
|
||||
return txParams{100000, big.NewInt(baseFee), nil, nil, nil}
|
||||
}),
|
||||
Entry("dynamic tx", func() txParams {
|
||||
return txParams{100000, nil, big.NewInt(baseFee), big.NewInt(0), ðtypes.AccessList{}}
|
||||
}),
|
||||
)
|
||||
DescribeTable("should not accept transactions with gas Limit > 0",
|
||||
func(malleate getprices) {
|
||||
p := malleate()
|
||||
to := tests.GenerateAddress()
|
||||
msgEthereumTx := buildEthTx(privKey, &to, p.gasLimit, p.gasPrice, p.gasFeeCap, p.gasTipCap, p.accesses)
|
||||
res := checkEthTx(privKey, msgEthereumTx)
|
||||
Expect(res.IsOK()).To(Equal(false), "transaction should have succeeded", res.GetLog())
|
||||
},
|
||||
Entry("legacy tx", func() txParams {
|
||||
return txParams{0, big.NewInt(baseFee), nil, nil, nil}
|
||||
}),
|
||||
Entry("dynamic tx", func() txParams {
|
||||
return txParams{0, nil, big.NewInt(baseFee), big.NewInt(0), ðtypes.AccessList{}}
|
||||
}),
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
// setupTestWithContext sets up a test chain with an example Cosmos send msg,
|
||||
// given a local (validator config) and a gloabl (feemarket param) minGasPrice
|
||||
func setupTestWithContext(valMinGasPrice string, minGasPrice sdk.Dec, baseFee sdk.Int) (*ethsecp256k1.PrivKey, banktypes.MsgSend) {
|
||||
privKey, msg := setupTest(valMinGasPrice + s.denom)
|
||||
params := types.DefaultParams()
|
||||
params.MinGasPrice = minGasPrice
|
||||
s.app.FeeMarketKeeper.SetParams(s.ctx, params)
|
||||
s.app.FeeMarketKeeper.SetBaseFee(s.ctx, baseFee.BigInt())
|
||||
s.Commit()
|
||||
|
||||
return privKey, msg
|
||||
}
|
||||
|
||||
func setupTest(localMinGasPrices string) (*ethsecp256k1.PrivKey, banktypes.MsgSend) {
|
||||
setupChain(localMinGasPrices)
|
||||
|
||||
privKey, address := generateKey()
|
||||
amount, ok := sdk.NewIntFromString("10000000000000000000")
|
||||
s.Require().True(ok)
|
||||
initBalance := sdk.Coins{sdk.Coin{
|
||||
Denom: s.denom,
|
||||
Amount: amount,
|
||||
}}
|
||||
testutil.FundAccount(s.app.BankKeeper, s.ctx, address, initBalance)
|
||||
|
||||
msg := banktypes.MsgSend{
|
||||
FromAddress: address.String(),
|
||||
ToAddress: address.String(),
|
||||
Amount: sdk.Coins{sdk.Coin{
|
||||
Denom: s.denom,
|
||||
Amount: sdk.NewInt(10000),
|
||||
}},
|
||||
}
|
||||
s.Commit()
|
||||
return privKey, msg
|
||||
}
|
||||
|
||||
func setupChain(localMinGasPricesStr string) {
|
||||
// Initialize the app, so we can use SetMinGasPrices to set the
|
||||
// validator-specific min-gas-prices setting
|
||||
db := dbm.NewMemDB()
|
||||
newapp := app.NewEthermintApp(
|
||||
log.NewNopLogger(),
|
||||
db,
|
||||
nil,
|
||||
true,
|
||||
map[int64]bool{},
|
||||
app.DefaultNodeHome,
|
||||
5,
|
||||
encoding.MakeConfig(app.ModuleBasics),
|
||||
simapp.EmptyAppOptions{},
|
||||
baseapp.SetMinGasPrices(localMinGasPricesStr),
|
||||
)
|
||||
|
||||
genesisState := app.NewTestGenesisState(newapp.AppCodec())
|
||||
genesisState[types.ModuleName] = newapp.AppCodec().MustMarshalJSON(types.DefaultGenesisState())
|
||||
|
||||
stateBytes, err := json.MarshalIndent(genesisState, "", " ")
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Initialize the chain
|
||||
newapp.InitChain(
|
||||
abci.RequestInitChain{
|
||||
ChainId: "ethermint_9000-1",
|
||||
Validators: []abci.ValidatorUpdate{},
|
||||
AppStateBytes: stateBytes,
|
||||
ConsensusParams: app.DefaultConsensusParams,
|
||||
},
|
||||
)
|
||||
|
||||
s.app = newapp
|
||||
s.SetupApp(false)
|
||||
}
|
||||
|
||||
func generateKey() (*ethsecp256k1.PrivKey, sdk.AccAddress) {
|
||||
address, priv := tests.NewAddrKey()
|
||||
return priv.(*ethsecp256k1.PrivKey), sdk.AccAddress(address.Bytes())
|
||||
}
|
||||
|
||||
func getNonce(addressBytes []byte) uint64 {
|
||||
return s.app.EvmKeeper.GetNonce(
|
||||
s.ctx,
|
||||
common.BytesToAddress(addressBytes),
|
||||
)
|
||||
}
|
||||
|
||||
func buildEthTx(
|
||||
priv *ethsecp256k1.PrivKey,
|
||||
to *common.Address,
|
||||
gasLimit uint64,
|
||||
gasPrice *big.Int,
|
||||
gasFeeCap *big.Int,
|
||||
gasTipCap *big.Int,
|
||||
accesses *ethtypes.AccessList,
|
||||
) *evmtypes.MsgEthereumTx {
|
||||
chainID := s.app.EvmKeeper.ChainID()
|
||||
from := common.BytesToAddress(priv.PubKey().Address().Bytes())
|
||||
nonce := getNonce(from.Bytes())
|
||||
data := make([]byte, 0)
|
||||
msgEthereumTx := evmtypes.NewTx(
|
||||
chainID,
|
||||
nonce,
|
||||
to,
|
||||
nil,
|
||||
gasLimit,
|
||||
gasPrice,
|
||||
gasFeeCap,
|
||||
gasTipCap,
|
||||
data,
|
||||
accesses,
|
||||
)
|
||||
msgEthereumTx.From = from.String()
|
||||
return msgEthereumTx
|
||||
}
|
||||
|
||||
func prepareEthTx(priv *ethsecp256k1.PrivKey, msgEthereumTx *evmtypes.MsgEthereumTx) []byte {
|
||||
encodingConfig := encoding.MakeConfig(app.ModuleBasics)
|
||||
option, err := codectypes.NewAnyWithValue(&evmtypes.ExtensionOptionsEthereumTx{})
|
||||
s.Require().NoError(err)
|
||||
|
||||
txBuilder := encodingConfig.TxConfig.NewTxBuilder()
|
||||
builder, ok := txBuilder.(authtx.ExtensionOptionsTxBuilder)
|
||||
s.Require().True(ok)
|
||||
builder.SetExtensionOptions(option)
|
||||
|
||||
err = msgEthereumTx.Sign(s.ethSigner, tests.NewSigner(priv))
|
||||
s.Require().NoError(err)
|
||||
|
||||
// A valid msg should have empty `From`
|
||||
msgEthereumTx.From = ""
|
||||
err = txBuilder.SetMsgs(msgEthereumTx)
|
||||
s.Require().NoError(err)
|
||||
|
||||
txData, err := evmtypes.UnpackTxData(msgEthereumTx.Data)
|
||||
s.Require().NoError(err)
|
||||
|
||||
evmDenom := s.app.EvmKeeper.GetParams(s.ctx).EvmDenom
|
||||
fees := sdk.Coins{{Denom: evmDenom, Amount: sdk.NewIntFromBigInt(txData.Fee())}}
|
||||
builder.SetFeeAmount(fees)
|
||||
builder.SetGasLimit(msgEthereumTx.GetGas())
|
||||
|
||||
// bz are bytes to be broadcasted over the network
|
||||
bz, err := encodingConfig.TxConfig.TxEncoder()(txBuilder.GetTx())
|
||||
s.Require().NoError(err)
|
||||
|
||||
return bz
|
||||
}
|
||||
|
||||
func checkEthTx(priv *ethsecp256k1.PrivKey, msgEthereumTx *evmtypes.MsgEthereumTx) abci.ResponseCheckTx {
|
||||
bz := prepareEthTx(priv, msgEthereumTx)
|
||||
req := abci.RequestCheckTx{Tx: bz}
|
||||
res := s.app.BaseApp.CheckTx(req)
|
||||
return res
|
||||
}
|
||||
|
||||
func deliverEthTx(priv *ethsecp256k1.PrivKey, msgEthereumTx *evmtypes.MsgEthereumTx) abci.ResponseDeliverTx {
|
||||
bz := prepareEthTx(priv, msgEthereumTx)
|
||||
req := abci.RequestDeliverTx{Tx: bz}
|
||||
res := s.app.BaseApp.DeliverTx(req)
|
||||
return res
|
||||
}
|
||||
+51
-18
@@ -19,6 +19,7 @@ import (
|
||||
ethermint "github.com/cerc-io/laconicd/types"
|
||||
"github.com/cerc-io/laconicd/x/evm/statedb"
|
||||
"github.com/cerc-io/laconicd/x/evm/types"
|
||||
evm "github.com/cerc-io/laconicd/x/evm/vm"
|
||||
)
|
||||
|
||||
// Keeper grants access to the EVM module state and implements the go-ethereum StateDB interface.
|
||||
@@ -54,14 +55,25 @@ type Keeper struct {
|
||||
|
||||
// EVM Hooks for tx post-processing
|
||||
hooks types.EvmHooks
|
||||
|
||||
// custom stateless precompiled smart contracts
|
||||
customPrecompiles evm.PrecompiledContracts
|
||||
|
||||
// evm constructor function
|
||||
evmConstructor evm.Constructor
|
||||
}
|
||||
|
||||
// NewKeeper generates new evm module keeper
|
||||
func NewKeeper(
|
||||
cdc codec.BinaryCodec,
|
||||
storeKey, transientKey storetypes.StoreKey, paramSpace paramtypes.Subspace,
|
||||
ak types.AccountKeeper, bankKeeper types.BankKeeper, sk types.StakingKeeper,
|
||||
storeKey, transientKey storetypes.StoreKey,
|
||||
paramSpace paramtypes.Subspace,
|
||||
ak types.AccountKeeper,
|
||||
bankKeeper types.BankKeeper,
|
||||
sk types.StakingKeeper,
|
||||
fmk types.FeeMarketKeeper,
|
||||
customPrecompiles evm.PrecompiledContracts,
|
||||
evmConstructor evm.Constructor,
|
||||
tracer string,
|
||||
) *Keeper {
|
||||
// ensure evm module account is set
|
||||
@@ -76,15 +88,17 @@ func NewKeeper(
|
||||
|
||||
// NOTE: we pass in the parameter space to the CommitStateDB in order to use custom denominations for the EVM operations
|
||||
return &Keeper{
|
||||
cdc: cdc,
|
||||
paramSpace: paramSpace,
|
||||
accountKeeper: ak,
|
||||
bankKeeper: bankKeeper,
|
||||
stakingKeeper: sk,
|
||||
feeMarketKeeper: fmk,
|
||||
storeKey: storeKey,
|
||||
transientKey: transientKey,
|
||||
tracer: tracer,
|
||||
cdc: cdc,
|
||||
paramSpace: paramSpace,
|
||||
accountKeeper: ak,
|
||||
bankKeeper: bankKeeper,
|
||||
stakingKeeper: sk,
|
||||
feeMarketKeeper: fmk,
|
||||
storeKey: storeKey,
|
||||
transientKey: transientKey,
|
||||
customPrecompiles: customPrecompiles,
|
||||
evmConstructor: evmConstructor,
|
||||
tracer: tracer,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -222,11 +236,11 @@ func (k *Keeper) SetHooks(eh types.EvmHooks) *Keeper {
|
||||
}
|
||||
|
||||
// PostTxProcessing delegate the call to the hooks. If no hook has been registered, this function returns with a `nil` error
|
||||
func (k *Keeper) PostTxProcessing(ctx sdk.Context, from common.Address, to *common.Address, receipt *ethtypes.Receipt) error {
|
||||
func (k *Keeper) PostTxProcessing(ctx sdk.Context, msg core.Message, receipt *ethtypes.Receipt) error {
|
||||
if k.hooks == nil {
|
||||
return nil
|
||||
}
|
||||
return k.hooks.PostTxProcessing(ctx, from, to, receipt)
|
||||
return k.hooks.PostTxProcessing(ctx, msg, receipt)
|
||||
}
|
||||
|
||||
// Tracer return a default vm.Tracer based on current keeper state
|
||||
@@ -283,17 +297,26 @@ func (k *Keeper) GetNonce(ctx sdk.Context, addr common.Address) uint64 {
|
||||
// GetBalance load account's balance of gas token
|
||||
func (k *Keeper) GetBalance(ctx sdk.Context, addr common.Address) *big.Int {
|
||||
cosmosAddr := sdk.AccAddress(addr.Bytes())
|
||||
params := k.GetParams(ctx)
|
||||
coin := k.bankKeeper.GetBalance(ctx, cosmosAddr, params.EvmDenom)
|
||||
evmDenom := ""
|
||||
k.paramSpace.GetIfExists(ctx, types.ParamStoreKeyEVMDenom, &evmDenom)
|
||||
// if node is pruned, params is empty. Return invalid value
|
||||
if evmDenom == "" {
|
||||
return big.NewInt(-1)
|
||||
}
|
||||
coin := k.bankKeeper.GetBalance(ctx, cosmosAddr, evmDenom)
|
||||
return coin.Amount.BigInt()
|
||||
}
|
||||
|
||||
// BaseFee returns current base fee, return values:
|
||||
// GetBaseFee returns current base fee, return values:
|
||||
// - `nil`: london hardfork not enabled.
|
||||
// - `0`: london hardfork enabled but feemarket is not enabled.
|
||||
// - `n`: both london hardfork and feemarket are enabled.
|
||||
func (k Keeper) BaseFee(ctx sdk.Context, ethCfg *params.ChainConfig) *big.Int {
|
||||
if !types.IsLondon(ethCfg, ctx.BlockHeight()) {
|
||||
func (k Keeper) GetBaseFee(ctx sdk.Context, ethCfg *params.ChainConfig) *big.Int {
|
||||
return k.getBaseFee(ctx, types.IsLondon(ethCfg, ctx.BlockHeight()))
|
||||
}
|
||||
|
||||
func (k Keeper) getBaseFee(ctx sdk.Context, london bool) *big.Int {
|
||||
if !london {
|
||||
return nil
|
||||
}
|
||||
baseFee := k.feeMarketKeeper.GetBaseFee(ctx)
|
||||
@@ -304,6 +327,16 @@ func (k Keeper) BaseFee(ctx sdk.Context, ethCfg *params.ChainConfig) *big.Int {
|
||||
return baseFee
|
||||
}
|
||||
|
||||
// GetMinGasMultiplier returns the MinGasMultiplier param from the fee market module
|
||||
func (k Keeper) GetMinGasMultiplier(ctx sdk.Context) sdk.Dec {
|
||||
fmkParmas := k.feeMarketKeeper.GetParams(ctx)
|
||||
if fmkParmas.MinGasMultiplier.IsNil() {
|
||||
// in case we are executing eth_call on a legacy block, returns a zero value.
|
||||
return sdk.ZeroDec()
|
||||
}
|
||||
return fmkParmas.MinGasMultiplier
|
||||
}
|
||||
|
||||
// ResetTransientGasUsed reset gas used to prepare for execution of current cosmos tx, called in ante handler.
|
||||
func (k Keeper) ResetTransientGasUsed(ctx sdk.Context) {
|
||||
store := ctx.TransientStore(k.transientKey)
|
||||
|
||||
+67
-28
@@ -8,8 +8,10 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
cmath "cosmossdk.io/math"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
sdkmath "cosmossdk.io/math"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
@@ -21,8 +23,9 @@ import (
|
||||
"github.com/cosmos/cosmos-sdk/simapp"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank/testutil"
|
||||
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
|
||||
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
|
||||
tmjson "github.com/tendermint/tendermint/libs/json"
|
||||
|
||||
"github.com/cerc-io/laconicd/app"
|
||||
"github.com/cerc-io/laconicd/crypto/ethsecp256k1"
|
||||
@@ -32,6 +35,7 @@ import (
|
||||
ethermint "github.com/cerc-io/laconicd/types"
|
||||
"github.com/cerc-io/laconicd/x/evm/statedb"
|
||||
"github.com/cerc-io/laconicd/x/evm/types"
|
||||
evmtypes "github.com/cerc-io/laconicd/x/evm/types"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
@@ -46,7 +50,7 @@ import (
|
||||
"github.com/tendermint/tendermint/version"
|
||||
)
|
||||
|
||||
var testTokens = cmath.NewIntWithDecimal(1000, 18)
|
||||
var testTokens = sdkmath.NewIntWithDecimal(1000, 18)
|
||||
|
||||
type KeeperTestSuite struct {
|
||||
suite.Suite
|
||||
@@ -67,12 +71,31 @@ type KeeperTestSuite struct {
|
||||
enableFeemarket bool
|
||||
enableLondonHF bool
|
||||
mintFeeCollector bool
|
||||
denom string
|
||||
}
|
||||
|
||||
/// DoSetupTest setup test environment, it uses`require.TestingT` to support both `testing.T` and `testing.B`.
|
||||
func (suite *KeeperTestSuite) DoSetupTest(t require.TestingT) {
|
||||
checkTx := false
|
||||
var s *KeeperTestSuite
|
||||
|
||||
func TestKeeperTestSuite(t *testing.T) {
|
||||
s = new(KeeperTestSuite)
|
||||
s.enableFeemarket = false
|
||||
s.enableLondonHF = true
|
||||
suite.Run(t, s)
|
||||
|
||||
// Run Ginkgo integration tests
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Keeper Suite")
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) SetupTest() {
|
||||
checkTx := false
|
||||
suite.app = app.Setup(checkTx, nil)
|
||||
suite.SetupApp(checkTx)
|
||||
}
|
||||
|
||||
// SetupApp setup test environment, it uses`require.TestingT` to support both `testing.T` and `testing.B`.
|
||||
func (suite *KeeperTestSuite) SetupApp(checkTx bool) {
|
||||
t := suite.T()
|
||||
// account key, use a constant account to keep unit test deterministic.
|
||||
ecdsaPriv, err := crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
require.NoError(t, err)
|
||||
@@ -87,7 +110,7 @@ func (suite *KeeperTestSuite) DoSetupTest(t require.TestingT) {
|
||||
require.NoError(t, err)
|
||||
suite.consAddress = sdk.ConsAddress(priv.PubKey().Address())
|
||||
|
||||
suite.app = app.Setup(suite.T(), checkTx, func(app *app.EthermintApp, genesis simapp.GenesisState) simapp.GenesisState {
|
||||
suite.app = app.Setup(checkTx, func(app *app.EthermintApp, genesis simapp.GenesisState) simapp.GenesisState {
|
||||
feemarketGenesis := feemarkettypes.DefaultGenesisState()
|
||||
if suite.enableFeemarket {
|
||||
feemarketGenesis.Params.EnableHeight = 1
|
||||
@@ -98,15 +121,48 @@ func (suite *KeeperTestSuite) DoSetupTest(t require.TestingT) {
|
||||
genesis[feemarkettypes.ModuleName] = app.AppCodec().MustMarshalJSON(feemarketGenesis)
|
||||
if !suite.enableLondonHF {
|
||||
evmGenesis := types.DefaultGenesisState()
|
||||
maxInt := sdk.NewInt(math.MaxInt64)
|
||||
maxInt := sdkmath.NewInt(math.MaxInt64)
|
||||
evmGenesis.Params.ChainConfig.LondonBlock = &maxInt
|
||||
evmGenesis.Params.ChainConfig.ArrowGlacierBlock = &maxInt
|
||||
evmGenesis.Params.ChainConfig.MergeForkBlock = &maxInt
|
||||
evmGenesis.Params.ChainConfig.GrayGlacierBlock = &maxInt
|
||||
evmGenesis.Params.ChainConfig.MergeNetsplitBlock = &maxInt
|
||||
genesis[types.ModuleName] = app.AppCodec().MustMarshalJSON(evmGenesis)
|
||||
}
|
||||
return genesis
|
||||
})
|
||||
|
||||
if suite.mintFeeCollector {
|
||||
// mint some coin to fee collector
|
||||
coins := sdk.NewCoins(sdk.NewCoin(types.DefaultEVMDenom, sdkmath.NewInt(int64(params.TxGas)-1)))
|
||||
genesisState := app.NewTestGenesisState(suite.app.AppCodec())
|
||||
balances := []banktypes.Balance{
|
||||
{
|
||||
Address: suite.app.AccountKeeper.GetModuleAddress(authtypes.FeeCollectorName).String(),
|
||||
Coins: coins,
|
||||
},
|
||||
}
|
||||
var bankGenesis banktypes.GenesisState
|
||||
suite.app.AppCodec().MustUnmarshalJSON(genesisState[banktypes.ModuleName], &bankGenesis)
|
||||
// Update balances and total supply
|
||||
bankGenesis.Balances = append(bankGenesis.Balances, balances...)
|
||||
bankGenesis.Supply = bankGenesis.Supply.Add(coins...)
|
||||
genesisState[banktypes.ModuleName] = suite.app.AppCodec().MustMarshalJSON(&bankGenesis)
|
||||
|
||||
// we marshal the genesisState of all module to a byte array
|
||||
stateBytes, err := tmjson.MarshalIndent(genesisState, "", " ")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Initialize the chain
|
||||
suite.app.InitChain(
|
||||
abci.RequestInitChain{
|
||||
ChainId: "ethermint_9000-1",
|
||||
Validators: []abci.ValidatorUpdate{},
|
||||
ConsensusParams: app.DefaultConsensusParams,
|
||||
AppStateBytes: stateBytes,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
suite.ctx = suite.app.BaseApp.NewContext(checkTx, tmproto.Header{
|
||||
Height: 1,
|
||||
ChainID: "ethermint_9000-1",
|
||||
@@ -131,12 +187,6 @@ func (suite *KeeperTestSuite) DoSetupTest(t require.TestingT) {
|
||||
LastResultsHash: tmhash.Sum([]byte("last_result")),
|
||||
})
|
||||
|
||||
if suite.mintFeeCollector {
|
||||
// mint some coin to fee collector
|
||||
coins := sdk.NewCoins(sdk.NewCoin(types.DefaultEVMDenom, sdk.NewInt(int64(params.TxGas)-1)))
|
||||
testutil.FundModuleAccount(suite.app.BankKeeper, suite.ctx, authtypes.FeeCollectorName, coins)
|
||||
}
|
||||
|
||||
queryHelper := baseapp.NewQueryServerTestHelper(suite.ctx, suite.app.InterfaceRegistry())
|
||||
types.RegisterQueryServer(queryHelper, suite.app.EvmKeeper)
|
||||
suite.queryClient = types.NewQueryClient(queryHelper)
|
||||
@@ -150,7 +200,6 @@ func (suite *KeeperTestSuite) DoSetupTest(t require.TestingT) {
|
||||
|
||||
valAddr := sdk.ValAddress(suite.address.Bytes())
|
||||
validator, err := stakingtypes.NewValidator(valAddr, priv.PubKey(), stakingtypes.Description{})
|
||||
require.NoError(t, err)
|
||||
err = suite.app.StakingKeeper.SetValidatorByConsAddr(suite.ctx, validator)
|
||||
require.NoError(t, err)
|
||||
err = suite.app.StakingKeeper.SetValidatorByConsAddr(suite.ctx, validator)
|
||||
@@ -161,10 +210,7 @@ func (suite *KeeperTestSuite) DoSetupTest(t require.TestingT) {
|
||||
suite.clientCtx = client.Context{}.WithTxConfig(encodingConfig.TxConfig)
|
||||
suite.ethSigner = ethtypes.LatestSignerForChainID(suite.app.EvmKeeper.ChainID())
|
||||
suite.appCodec = encodingConfig.Codec
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) SetupTest() {
|
||||
suite.DoSetupTest(suite.T())
|
||||
suite.denom = evmtypes.DefaultEVMDenom
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) EvmDenom() string {
|
||||
@@ -381,17 +427,10 @@ func (suite *KeeperTestSuite) TestBaseFee() {
|
||||
suite.app.EvmKeeper.BeginBlock(suite.ctx, abci.RequestBeginBlock{})
|
||||
params := suite.app.EvmKeeper.GetParams(suite.ctx)
|
||||
ethCfg := params.ChainConfig.EthereumConfig(suite.app.EvmKeeper.ChainID())
|
||||
baseFee := suite.app.EvmKeeper.BaseFee(suite.ctx, ethCfg)
|
||||
baseFee := suite.app.EvmKeeper.GetBaseFee(suite.ctx, ethCfg)
|
||||
suite.Require().Equal(tc.expectBaseFee, baseFee)
|
||||
})
|
||||
}
|
||||
suite.enableFeemarket = false
|
||||
suite.enableLondonHF = true
|
||||
}
|
||||
|
||||
func TestKeeperTestSuite(t *testing.T) {
|
||||
suite.Run(t, &KeeperTestSuite{
|
||||
enableFeemarket: false,
|
||||
enableLondonHF: true,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
package keeper
|
||||
|
||||
import (
|
||||
v2 "github.com/cerc-io/laconicd/x/evm/migrations/v2"
|
||||
v3 "github.com/cerc-io/laconicd/x/evm/migrations/v3"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// Migrator is a struct for handling in-place store migrations.
|
||||
type Migrator struct {
|
||||
keeper Keeper
|
||||
@@ -11,3 +17,13 @@ func NewMigrator(keeper Keeper) Migrator {
|
||||
keeper: keeper,
|
||||
}
|
||||
}
|
||||
|
||||
// Migrate1to2 migrates the store from consensus version v1 to v2
|
||||
func (m Migrator) Migrate1to2(ctx sdk.Context) error {
|
||||
return v2.MigrateStore(ctx, &m.keeper.paramSpace)
|
||||
}
|
||||
|
||||
// Migrate2to3 migrates the store from consensus version v2 to v3
|
||||
func (m Migrator) Migrate2to3(ctx sdk.Context) error {
|
||||
return v3.MigrateStore(ctx, &m.keeper.paramSpace)
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ import (
|
||||
tmbytes "github.com/tendermint/tendermint/libs/bytes"
|
||||
tmtypes "github.com/tendermint/tendermint/types"
|
||||
|
||||
"github.com/armon/go-metrics"
|
||||
"github.com/cosmos/cosmos-sdk/telemetry"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
|
||||
@@ -19,7 +21,8 @@ var _ types.MsgServer = &Keeper{}
|
||||
|
||||
// EthereumTx implements the gRPC MsgServer interface. It receives a transaction which is then
|
||||
// executed (i.e applied) against the go-ethereum EVM. The provided SDK Context is set to the Keeper
|
||||
// so that it can call the StateDB methods without receiving it as a function parameter.
|
||||
// so that it can implements and call the StateDB methods without receiving it as a function
|
||||
// parameter.
|
||||
func (k *Keeper) EthereumTx(goCtx context.Context, msg *types.MsgEthereumTx) (*types.MsgEthereumTxResponse, error) {
|
||||
ctx := sdk.UnwrapSDKContext(goCtx)
|
||||
|
||||
@@ -27,11 +30,52 @@ func (k *Keeper) EthereumTx(goCtx context.Context, msg *types.MsgEthereumTx) (*t
|
||||
tx := msg.AsTransaction()
|
||||
txIndex := k.GetTxIndexTransient(ctx)
|
||||
|
||||
labels := []metrics.Label{
|
||||
telemetry.NewLabel("tx_type", fmt.Sprintf("%d", tx.Type())),
|
||||
telemetry.NewLabel("from", sender),
|
||||
}
|
||||
if tx.To() == nil {
|
||||
labels = []metrics.Label{telemetry.NewLabel("execution", "create")}
|
||||
} else {
|
||||
labels = []metrics.Label{
|
||||
telemetry.NewLabel("execution", "call"),
|
||||
telemetry.NewLabel("to", tx.To().Hex()), // recipient address (contract or account)
|
||||
}
|
||||
}
|
||||
|
||||
response, err := k.ApplyTransaction(ctx, tx)
|
||||
if err != nil {
|
||||
return nil, sdkerrors.Wrap(err, "failed to apply transaction")
|
||||
}
|
||||
|
||||
defer func() {
|
||||
telemetry.IncrCounterWithLabels(
|
||||
[]string{"tx", "msg", "ethereum_tx", "total"},
|
||||
1,
|
||||
labels,
|
||||
)
|
||||
|
||||
if response.GasUsed != 0 {
|
||||
telemetry.IncrCounterWithLabels(
|
||||
[]string{"tx", "msg", "ethereum_tx", "gas_used", "total"},
|
||||
float32(response.GasUsed),
|
||||
labels,
|
||||
)
|
||||
|
||||
// Observe which users define a gas limit >> gas used. Note, that
|
||||
// gas_limit and gas_used are always > 0
|
||||
gasLimit := sdk.NewDec(int64(tx.Gas()))
|
||||
gasRatio, err := gasLimit.QuoInt64(int64(response.GasUsed)).Float64()
|
||||
if err == nil {
|
||||
telemetry.SetGaugeWithLabels(
|
||||
[]string{"tx", "msg", "ethereum_tx", "gas_limit", "per", "gas_used"},
|
||||
float32(gasRatio),
|
||||
labels,
|
||||
)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
attrs := []sdk.Attribute{
|
||||
sdk.NewAttribute(sdk.AttributeKeyAmount, tx.Value().String()),
|
||||
// add event for ethereum transaction hash format
|
||||
|
||||
@@ -8,7 +8,12 @@ import (
|
||||
|
||||
// GetParams returns the total set of evm parameters.
|
||||
func (k Keeper) GetParams(ctx sdk.Context) (params types.Params) {
|
||||
k.paramSpace.GetParamSet(ctx, ¶ms)
|
||||
// TODO: update once https://github.com/cosmos/cosmos-sdk/pull/12615 is merged
|
||||
// and released
|
||||
for _, pair := range params.ParamSetPairs() {
|
||||
k.paramSpace.GetIfExists(ctx, pair.Key, pair.Value)
|
||||
}
|
||||
|
||||
return params
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"math"
|
||||
"math/big"
|
||||
|
||||
sdkmath "cosmossdk.io/math"
|
||||
|
||||
tmtypes "github.com/tendermint/tendermint/types"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
@@ -14,6 +16,7 @@ import (
|
||||
ethermint "github.com/cerc-io/laconicd/types"
|
||||
"github.com/cerc-io/laconicd/x/evm/statedb"
|
||||
"github.com/cerc-io/laconicd/x/evm/types"
|
||||
evm "github.com/cerc-io/laconicd/x/evm/vm"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
@@ -46,7 +49,7 @@ func (k *Keeper) EVMConfig(ctx sdk.Context) (*types.EVMConfig, error) {
|
||||
return nil, sdkerrors.Wrap(err, "failed to obtain coinbase address")
|
||||
}
|
||||
|
||||
baseFee := k.BaseFee(ctx, ethCfg)
|
||||
baseFee := k.GetBaseFee(ctx, ethCfg)
|
||||
return &types.EVMConfig{
|
||||
Params: params,
|
||||
ChainConfig: ethCfg,
|
||||
@@ -55,7 +58,7 @@ func (k *Keeper) EVMConfig(ctx sdk.Context) (*types.EVMConfig, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// TxConfig load `TxConfig` from current transient storage
|
||||
// TxConfig loads `TxConfig` from current transient storage
|
||||
func (k *Keeper) TxConfig(ctx sdk.Context, txHash common.Hash) statedb.TxConfig {
|
||||
return statedb.NewTxConfig(
|
||||
common.BytesToHash(ctx.HeaderHash()), // BlockHash
|
||||
@@ -75,7 +78,7 @@ func (k *Keeper) NewEVM(
|
||||
cfg *types.EVMConfig,
|
||||
tracer vm.EVMLogger,
|
||||
stateDB vm.StateDB,
|
||||
) *vm.EVM {
|
||||
) evm.EVM {
|
||||
blockCtx := vm.BlockContext{
|
||||
CanTransfer: core.CanTransfer,
|
||||
Transfer: core.Transfer,
|
||||
@@ -93,7 +96,7 @@ func (k *Keeper) NewEVM(
|
||||
tracer = k.Tracer(ctx, msg, cfg.ChainConfig)
|
||||
}
|
||||
vmConfig := k.VMConfig(ctx, msg, cfg, tracer)
|
||||
return vm.NewEVM(blockCtx, txCtx, stateDB, cfg.ChainConfig, vmConfig)
|
||||
return k.evmConstructor(blockCtx, txCtx, stateDB, cfg.ChainConfig, vmConfig, k.customPrecompiles)
|
||||
}
|
||||
|
||||
// VMConfig creates an EVM configuration from the debug setting and the extra EIPs enabled on the
|
||||
@@ -176,7 +179,7 @@ func (k Keeper) GetHashFn(ctx sdk.Context) vm.GetHashFunc {
|
||||
// ApplyTransaction runs and attempts to perform a state transition with the given transaction (i.e Message), that will
|
||||
// only be persisted (committed) to the underlying KVStore if the transaction does not fail.
|
||||
//
|
||||
// Gas tracking
|
||||
// # Gas tracking
|
||||
//
|
||||
// Ethereum consumes gas according to the EVM opcodes instead of general reads and writes to store. Because of this, the
|
||||
// state transition needs to ignore the SDK gas consumption mechanism defined by the GasKVStore and instead consume the
|
||||
@@ -235,42 +238,47 @@ func (k *Keeper) ApplyTransaction(ctx sdk.Context, tx *ethtypes.Transaction) (*t
|
||||
bloomReceipt = ethtypes.BytesToBloom(bloom.Bytes())
|
||||
}
|
||||
|
||||
cumulativeGasUsed := res.GasUsed
|
||||
if ctx.BlockGasMeter() != nil {
|
||||
limit := ctx.BlockGasMeter().Limit()
|
||||
consumed := ctx.BlockGasMeter().GasConsumed()
|
||||
cumulativeGasUsed = uint64(math.Min(float64(cumulativeGasUsed+consumed), float64(limit)))
|
||||
}
|
||||
|
||||
var contractAddr common.Address
|
||||
if msg.To() == nil {
|
||||
contractAddr = crypto.CreateAddress(msg.From(), msg.Nonce())
|
||||
}
|
||||
|
||||
receipt := ðtypes.Receipt{
|
||||
Type: tx.Type(),
|
||||
PostState: nil, // TODO: intermediate state root
|
||||
CumulativeGasUsed: cumulativeGasUsed,
|
||||
Bloom: bloomReceipt,
|
||||
Logs: logs,
|
||||
TxHash: txConfig.TxHash,
|
||||
ContractAddress: contractAddr,
|
||||
GasUsed: res.GasUsed,
|
||||
BlockHash: txConfig.BlockHash,
|
||||
BlockNumber: big.NewInt(ctx.BlockHeight()),
|
||||
TransactionIndex: txConfig.TxIndex,
|
||||
}
|
||||
|
||||
if !res.Failed() {
|
||||
cumulativeGasUsed := res.GasUsed
|
||||
if ctx.BlockGasMeter() != nil {
|
||||
limit := ctx.BlockGasMeter().Limit()
|
||||
consumed := ctx.BlockGasMeter().GasConsumed()
|
||||
cumulativeGasUsed = uint64(math.Min(float64(cumulativeGasUsed+consumed), float64(limit)))
|
||||
}
|
||||
|
||||
var contractAddr common.Address
|
||||
if msg.To() == nil {
|
||||
contractAddr = crypto.CreateAddress(msg.From(), msg.Nonce())
|
||||
}
|
||||
|
||||
receipt := ðtypes.Receipt{
|
||||
Type: tx.Type(),
|
||||
PostState: nil, // TODO: intermediate state root
|
||||
Status: ethtypes.ReceiptStatusSuccessful,
|
||||
CumulativeGasUsed: cumulativeGasUsed,
|
||||
Bloom: bloomReceipt,
|
||||
Logs: logs,
|
||||
TxHash: txConfig.TxHash,
|
||||
ContractAddress: contractAddr,
|
||||
GasUsed: res.GasUsed,
|
||||
BlockHash: txConfig.BlockHash,
|
||||
BlockNumber: big.NewInt(ctx.BlockHeight()),
|
||||
TransactionIndex: txConfig.TxIndex,
|
||||
}
|
||||
|
||||
receipt.Status = ethtypes.ReceiptStatusSuccessful
|
||||
// Only call hooks if tx executed successfully.
|
||||
if err = k.PostTxProcessing(tmpCtx, msg.From(), tx.To(), receipt); err != nil {
|
||||
if err = k.PostTxProcessing(tmpCtx, msg, receipt); err != nil {
|
||||
// If hooks return error, revert the whole tx.
|
||||
res.VmError = types.ErrPostTxProcessing.Error()
|
||||
k.Logger(ctx).Error("tx post processing failed", "error", err)
|
||||
|
||||
// If the tx failed in post processing hooks, we should clear the logs
|
||||
res.Logs = nil
|
||||
} else if commit != nil {
|
||||
// PostTxProcessing is successful, commit the tmpCtx
|
||||
commit()
|
||||
// Since the post processing can alter the log, we need to update the result
|
||||
res.Logs = types.NewLogsFromEth(receipt.Logs)
|
||||
ctx.EventManager().EmitEvents(tmpCtx.EventManager().Events())
|
||||
}
|
||||
}
|
||||
@@ -280,11 +288,10 @@ func (k *Keeper) ApplyTransaction(ctx sdk.Context, tx *ethtypes.Transaction) (*t
|
||||
return nil, sdkerrors.Wrapf(err, "failed to refund gas leftover gas to sender %s", msg.From())
|
||||
}
|
||||
|
||||
if len(logs) > 0 {
|
||||
if len(receipt.Logs) > 0 {
|
||||
// Update transient block bloom filter
|
||||
k.SetBlockBloomTransient(ctx, bloom)
|
||||
|
||||
k.SetLogSizeTransient(ctx, uint64(txConfig.LogIndex)+uint64(len(logs)))
|
||||
k.SetBlockBloomTransient(ctx, receipt.Bloom.Big())
|
||||
k.SetLogSizeTransient(ctx, uint64(txConfig.LogIndex)+uint64(len(receipt.Logs)))
|
||||
}
|
||||
|
||||
k.SetTxIndexTransient(ctx, uint64(txConfig.TxIndex)+1)
|
||||
@@ -303,18 +310,18 @@ func (k *Keeper) ApplyTransaction(ctx sdk.Context, tx *ethtypes.Transaction) (*t
|
||||
// If the message fails, the VM execution error with the reason will be returned to the client
|
||||
// and the transaction won't be committed to the store.
|
||||
//
|
||||
// Reverted state
|
||||
// # Reverted state
|
||||
//
|
||||
// The snapshot and rollback are supported by the `statedb.StateDB`.
|
||||
//
|
||||
// Different Callers
|
||||
// # Different Callers
|
||||
//
|
||||
// It's called in three scenarios:
|
||||
// 1. `ApplyTransaction`, in the transaction processing flow.
|
||||
// 2. `EthCall/EthEstimateGas` grpc query handler.
|
||||
// 3. Called by other native modules directly.
|
||||
//
|
||||
// Prechecks and Preprocessing
|
||||
// # Prechecks and Preprocessing
|
||||
//
|
||||
// All relevant state transition prechecks for the MsgEthereumTx are performed on the AnteHandler,
|
||||
// prior to running the transaction against the state. The prechecks run are the following:
|
||||
@@ -330,14 +337,20 @@ func (k *Keeper) ApplyTransaction(ctx sdk.Context, tx *ethtypes.Transaction) (*t
|
||||
//
|
||||
// 1. set up the initial access list (iff fork > Berlin)
|
||||
//
|
||||
// Tracer parameter
|
||||
// # Tracer parameter
|
||||
//
|
||||
// It should be a `vm.Tracer` object or nil, if pass `nil`, it'll create a default one based on keeper options.
|
||||
//
|
||||
// Commit parameter
|
||||
// # Commit parameter
|
||||
//
|
||||
// If commit is true, the `StateDB` will be committed, otherwise discarded.
|
||||
func (k *Keeper) ApplyMessageWithConfig(ctx sdk.Context, msg core.Message, tracer vm.EVMLogger, commit bool, cfg *types.EVMConfig, txConfig statedb.TxConfig) (*types.MsgEthereumTxResponse, error) {
|
||||
func (k *Keeper) ApplyMessageWithConfig(ctx sdk.Context,
|
||||
msg core.Message,
|
||||
tracer vm.EVMLogger,
|
||||
commit bool,
|
||||
cfg *types.EVMConfig,
|
||||
txConfig statedb.TxConfig,
|
||||
) (*types.MsgEthereumTxResponse, error) {
|
||||
var (
|
||||
ret []byte // return bytes from evm execution
|
||||
vmErr error // vm errors do not effect consensus and are therefore not assigned to err
|
||||
@@ -353,26 +366,38 @@ func (k *Keeper) ApplyMessageWithConfig(ctx sdk.Context, msg core.Message, trace
|
||||
stateDB := statedb.New(ctx, k, txConfig)
|
||||
evm := k.NewEVM(ctx, msg, cfg, tracer, stateDB)
|
||||
|
||||
leftoverGas := msg.Gas()
|
||||
|
||||
// Allow the tracer captures the tx level events, mainly the gas consumption.
|
||||
vmCfg := evm.Config()
|
||||
if vmCfg.Debug {
|
||||
vmCfg.Tracer.CaptureTxStart(leftoverGas)
|
||||
defer func() {
|
||||
vmCfg.Tracer.CaptureTxEnd(leftoverGas)
|
||||
}()
|
||||
}
|
||||
|
||||
sender := vm.AccountRef(msg.From())
|
||||
contractCreation := msg.To() == nil
|
||||
isLondon := cfg.ChainConfig.IsLondon(evm.Context.BlockNumber)
|
||||
isLondon := cfg.ChainConfig.IsLondon(evm.Context().BlockNumber)
|
||||
|
||||
intrinsicGas, err := k.GetEthIntrinsicGas(ctx, msg, cfg.ChainConfig, contractCreation)
|
||||
if err != nil {
|
||||
// should have already been checked on Ante Handler
|
||||
return nil, sdkerrors.Wrap(err, "intrinsic gas failed")
|
||||
}
|
||||
|
||||
// Should check again even if it is checked on Ante Handler, because eth_call don't go through Ante Handler.
|
||||
if msg.Gas() < intrinsicGas {
|
||||
if leftoverGas < intrinsicGas {
|
||||
// eth_estimateGas will check for this exact error
|
||||
return nil, sdkerrors.Wrap(core.ErrIntrinsicGas, "apply message")
|
||||
}
|
||||
leftoverGas := msg.Gas() - intrinsicGas
|
||||
leftoverGas -= intrinsicGas
|
||||
|
||||
// access list preparation is moved from ante handler to here, because it's needed when `ApplyMessage` is called
|
||||
// under contexts where ante handlers are not run, for example `eth_call` and `eth_estimateGas`.
|
||||
if rules := cfg.ChainConfig.Rules(big.NewInt(ctx.BlockHeight()), cfg.ChainConfig.MergeForkBlock != nil); rules.IsBerlin {
|
||||
stateDB.PrepareAccessList(msg.From(), msg.To(), vm.ActivePrecompiles(rules), msg.AccessList())
|
||||
if rules := cfg.ChainConfig.Rules(big.NewInt(ctx.BlockHeight()), cfg.ChainConfig.MergeNetsplitBlock != nil); rules.IsBerlin {
|
||||
stateDB.PrepareAccessList(msg.From(), msg.To(), evm.ActivePrecompiles(rules), msg.AccessList())
|
||||
}
|
||||
|
||||
if contractCreation {
|
||||
@@ -397,12 +422,8 @@ func (k *Keeper) ApplyMessageWithConfig(ctx sdk.Context, msg core.Message, trace
|
||||
if msg.Gas() < leftoverGas {
|
||||
return nil, sdkerrors.Wrap(types.ErrGasOverflow, "apply message")
|
||||
}
|
||||
gasUsed := msg.Gas() - leftoverGas
|
||||
refund := GasToRefund(stateDB.GetRefund(), gasUsed, refundQuotient)
|
||||
if refund > gasUsed {
|
||||
return nil, sdkerrors.Wrap(types.ErrGasOverflow, "apply message")
|
||||
}
|
||||
gasUsed -= refund
|
||||
// refund gas
|
||||
leftoverGas += GasToRefund(stateDB.GetRefund(), msg.Gas()-leftoverGas, refundQuotient)
|
||||
|
||||
// EVM execution error needs to be available for the JSON-RPC client
|
||||
var vmError string
|
||||
@@ -417,6 +438,21 @@ func (k *Keeper) ApplyMessageWithConfig(ctx sdk.Context, msg core.Message, trace
|
||||
}
|
||||
}
|
||||
|
||||
// calculate a minimum amount of gas to be charged to sender if GasLimit
|
||||
// is considerably higher than GasUsed to stay more aligned with Tendermint gas mechanics
|
||||
// for more info https://github.com/cerc-io/laconicd/issues/1085
|
||||
gasLimit := sdk.NewDec(int64(msg.Gas()))
|
||||
minGasMultiplier := k.GetMinGasMultiplier(ctx)
|
||||
minimumGasUsed := gasLimit.Mul(minGasMultiplier)
|
||||
|
||||
if msg.Gas() < leftoverGas {
|
||||
return nil, sdkerrors.Wrapf(types.ErrGasOverflow, "message gas limit < leftover gas (%d < %d)", msg.Gas(), leftoverGas)
|
||||
}
|
||||
temporaryGasUsed := msg.Gas() - leftoverGas
|
||||
gasUsed := sdk.MaxDec(minimumGasUsed, sdk.NewDec(int64(temporaryGasUsed))).TruncateInt().Uint64()
|
||||
// reset leftoverGas, to be used by the tracer
|
||||
leftoverGas = msg.Gas() - gasUsed
|
||||
|
||||
return &types.MsgEthereumTxResponse{
|
||||
GasUsed: gasUsed,
|
||||
VmError: vmError,
|
||||
@@ -459,7 +495,7 @@ func (k *Keeper) RefundGas(ctx sdk.Context, msg core.Message, leftoverGas uint64
|
||||
return sdkerrors.Wrapf(types.ErrInvalidRefund, "refunded amount value cannot be negative %d", remaining.Int64())
|
||||
case 1:
|
||||
// positive amount refund
|
||||
refundedCoins := sdk.Coins{sdk.NewCoin(denom, sdk.NewIntFromBigInt(remaining))}
|
||||
refundedCoins := sdk.Coins{sdk.NewCoin(denom, sdkmath.NewIntFromBigInt(remaining))}
|
||||
|
||||
// refund to sender from the fee collector module account, which is the escrow account in charge of collecting tx fees
|
||||
|
||||
|
||||
@@ -143,7 +143,7 @@ func newNativeMessage(
|
||||
|
||||
func BenchmarkApplyTransaction(b *testing.B) {
|
||||
suite := KeeperTestSuite{enableLondonHF: true}
|
||||
suite.DoSetupTest(b)
|
||||
suite.SetupTest()
|
||||
|
||||
ethSigner := ethtypes.LatestSignerForChainID(suite.app.EvmKeeper.ChainID())
|
||||
|
||||
@@ -170,7 +170,7 @@ func BenchmarkApplyTransaction(b *testing.B) {
|
||||
|
||||
func BenchmarkApplyTransactionWithLegacyTx(b *testing.B) {
|
||||
suite := KeeperTestSuite{enableLondonHF: true}
|
||||
suite.DoSetupTest(b)
|
||||
suite.SetupTest()
|
||||
|
||||
ethSigner := ethtypes.LatestSignerForChainID(suite.app.EvmKeeper.ChainID())
|
||||
|
||||
@@ -197,7 +197,7 @@ func BenchmarkApplyTransactionWithLegacyTx(b *testing.B) {
|
||||
|
||||
func BenchmarkApplyTransactionWithDynamicFeeTx(b *testing.B) {
|
||||
suite := KeeperTestSuite{enableFeemarket: true, enableLondonHF: true}
|
||||
suite.DoSetupTest(b)
|
||||
suite.SetupTest()
|
||||
|
||||
ethSigner := ethtypes.LatestSignerForChainID(suite.app.EvmKeeper.ChainID())
|
||||
|
||||
@@ -224,7 +224,7 @@ func BenchmarkApplyTransactionWithDynamicFeeTx(b *testing.B) {
|
||||
|
||||
func BenchmarkApplyMessage(b *testing.B) {
|
||||
suite := KeeperTestSuite{enableLondonHF: true}
|
||||
suite.DoSetupTest(b)
|
||||
suite.SetupTest()
|
||||
|
||||
params := suite.app.EvmKeeper.GetParams(suite.ctx)
|
||||
ethCfg := params.ChainConfig.EthereumConfig(suite.app.EvmKeeper.ChainID())
|
||||
@@ -259,7 +259,7 @@ func BenchmarkApplyMessage(b *testing.B) {
|
||||
|
||||
func BenchmarkApplyMessageWithLegacyTx(b *testing.B) {
|
||||
suite := KeeperTestSuite{enableLondonHF: true}
|
||||
suite.DoSetupTest(b)
|
||||
suite.SetupTest()
|
||||
|
||||
params := suite.app.EvmKeeper.GetParams(suite.ctx)
|
||||
ethCfg := params.ChainConfig.EthereumConfig(suite.app.EvmKeeper.ChainID())
|
||||
@@ -294,7 +294,7 @@ func BenchmarkApplyMessageWithLegacyTx(b *testing.B) {
|
||||
|
||||
func BenchmarkApplyMessageWithDynamicFeeTx(b *testing.B) {
|
||||
suite := KeeperTestSuite{enableFeemarket: true, enableLondonHF: true}
|
||||
suite.DoSetupTest(b)
|
||||
suite.SetupTest()
|
||||
|
||||
params := suite.app.EvmKeeper.GetParams(suite.ctx)
|
||||
ethCfg := params.ChainConfig.EthereumConfig(suite.app.EvmKeeper.ChainID())
|
||||
|
||||
@@ -206,16 +206,6 @@ func (suite *KeeperTestSuite) TestGetEthIntrinsicGas() {
|
||||
true,
|
||||
params.TxGas + params.TxDataNonZeroGasFrontier*1,
|
||||
},
|
||||
// we are not able to test the ErrGasUintOverflow due to RAM limitation
|
||||
// {
|
||||
// "with big data size overflow",
|
||||
// make([]byte, 271300000000000000),
|
||||
// nil,
|
||||
// 1,
|
||||
// false,
|
||||
// false,
|
||||
// 0,
|
||||
// },
|
||||
{
|
||||
"no data, one accesslist, not contract creation, not homestead, not istanbul",
|
||||
nil,
|
||||
|
||||
+5
-10
@@ -1,10 +1,11 @@
|
||||
package keeper
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
sdkmath "cosmossdk.io/math"
|
||||
|
||||
ethermint "github.com/cerc-io/laconicd/types"
|
||||
"github.com/cerc-io/laconicd/x/evm/statedb"
|
||||
"github.com/cerc-io/laconicd/x/evm/types"
|
||||
@@ -79,7 +80,7 @@ func (k *Keeper) SetBalance(ctx sdk.Context, addr common.Address, amount *big.In
|
||||
switch delta.Sign() {
|
||||
case 1:
|
||||
// mint
|
||||
coins := sdk.NewCoins(sdk.NewCoin(params.EvmDenom, sdk.NewIntFromBigInt(delta)))
|
||||
coins := sdk.NewCoins(sdk.NewCoin(params.EvmDenom, sdkmath.NewIntFromBigInt(delta)))
|
||||
if err := k.bankKeeper.MintCoins(ctx, types.ModuleName, coins); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -88,7 +89,7 @@ func (k *Keeper) SetBalance(ctx sdk.Context, addr common.Address, amount *big.In
|
||||
}
|
||||
case -1:
|
||||
// burn
|
||||
coins := sdk.NewCoins(sdk.NewCoin(params.EvmDenom, sdk.NewIntFromBigInt(new(big.Int).Neg(delta))))
|
||||
coins := sdk.NewCoins(sdk.NewCoin(params.EvmDenom, sdkmath.NewIntFromBigInt(new(big.Int).Neg(delta))))
|
||||
if err := k.bankKeeper.SendCoinsFromAccountToModule(ctx, cosmosAddr, types.ModuleName, coins); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -186,7 +187,7 @@ func (k *Keeper) DeleteAccount(ctx sdk.Context, addr common.Address) error {
|
||||
}
|
||||
|
||||
// NOTE: only Ethereum accounts (contracts) can be selfdestructed
|
||||
ethAcct, ok := acct.(ethermint.EthAccountI)
|
||||
_, ok := acct.(ethermint.EthAccountI)
|
||||
if !ok {
|
||||
return sdkerrors.Wrapf(types.ErrInvalidAccount, "type %T, address %s", acct, addr)
|
||||
}
|
||||
@@ -196,12 +197,6 @@ func (k *Keeper) DeleteAccount(ctx sdk.Context, addr common.Address) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// remove code
|
||||
codeHashBz := ethAcct.GetCodeHash().Bytes()
|
||||
if !bytes.Equal(codeHashBz, types.EmptyCodeHash) {
|
||||
k.SetCode(ctx, codeHashBz, nil)
|
||||
}
|
||||
|
||||
// clear storage
|
||||
k.ForEachStorage(ctx, addr, func(key, _ common.Hash) bool {
|
||||
k.SetState(ctx, addr, key, nil)
|
||||
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
|
||||
func BenchmarkCreateAccountNew(b *testing.B) {
|
||||
suite := KeeperTestSuite{}
|
||||
suite.DoSetupTest(b)
|
||||
suite.SetupTest()
|
||||
vmdb := suite.StateDB()
|
||||
|
||||
b.ResetTimer()
|
||||
@@ -31,7 +31,7 @@ func BenchmarkCreateAccountNew(b *testing.B) {
|
||||
|
||||
func BenchmarkCreateAccountExisting(b *testing.B) {
|
||||
suite := KeeperTestSuite{}
|
||||
suite.DoSetupTest(b)
|
||||
suite.SetupTest()
|
||||
vmdb := suite.StateDB()
|
||||
|
||||
b.ResetTimer()
|
||||
@@ -44,7 +44,7 @@ func BenchmarkCreateAccountExisting(b *testing.B) {
|
||||
|
||||
func BenchmarkAddBalance(b *testing.B) {
|
||||
suite := KeeperTestSuite{}
|
||||
suite.DoSetupTest(b)
|
||||
suite.SetupTest()
|
||||
vmdb := suite.StateDB()
|
||||
|
||||
amt := big.NewInt(10)
|
||||
@@ -59,7 +59,7 @@ func BenchmarkAddBalance(b *testing.B) {
|
||||
|
||||
func BenchmarkSetCode(b *testing.B) {
|
||||
suite := KeeperTestSuite{}
|
||||
suite.DoSetupTest(b)
|
||||
suite.SetupTest()
|
||||
vmdb := suite.StateDB()
|
||||
|
||||
hash := crypto.Keccak256Hash([]byte("code")).Bytes()
|
||||
@@ -74,7 +74,7 @@ func BenchmarkSetCode(b *testing.B) {
|
||||
|
||||
func BenchmarkSetState(b *testing.B) {
|
||||
suite := KeeperTestSuite{}
|
||||
suite.DoSetupTest(b)
|
||||
suite.SetupTest()
|
||||
vmdb := suite.StateDB()
|
||||
|
||||
hash := crypto.Keccak256Hash([]byte("topic")).Bytes()
|
||||
@@ -89,7 +89,7 @@ func BenchmarkSetState(b *testing.B) {
|
||||
|
||||
func BenchmarkAddLog(b *testing.B) {
|
||||
suite := KeeperTestSuite{}
|
||||
suite.DoSetupTest(b)
|
||||
suite.SetupTest()
|
||||
vmdb := suite.StateDB()
|
||||
|
||||
topic := crypto.Keccak256Hash([]byte("topic"))
|
||||
@@ -116,7 +116,7 @@ func BenchmarkAddLog(b *testing.B) {
|
||||
|
||||
func BenchmarkSnapshot(b *testing.B) {
|
||||
suite := KeeperTestSuite{}
|
||||
suite.DoSetupTest(b)
|
||||
suite.SetupTest()
|
||||
vmdb := suite.StateDB()
|
||||
|
||||
b.ResetTimer()
|
||||
@@ -136,7 +136,7 @@ func BenchmarkSnapshot(b *testing.B) {
|
||||
|
||||
func BenchmarkSubBalance(b *testing.B) {
|
||||
suite := KeeperTestSuite{}
|
||||
suite.DoSetupTest(b)
|
||||
suite.SetupTest()
|
||||
vmdb := suite.StateDB()
|
||||
|
||||
amt := big.NewInt(10)
|
||||
@@ -151,7 +151,7 @@ func BenchmarkSubBalance(b *testing.B) {
|
||||
|
||||
func BenchmarkSetNonce(b *testing.B) {
|
||||
suite := KeeperTestSuite{}
|
||||
suite.DoSetupTest(b)
|
||||
suite.SetupTest()
|
||||
vmdb := suite.StateDB()
|
||||
|
||||
b.ResetTimer()
|
||||
@@ -164,7 +164,7 @@ func BenchmarkSetNonce(b *testing.B) {
|
||||
|
||||
func BenchmarkAddRefund(b *testing.B) {
|
||||
suite := KeeperTestSuite{}
|
||||
suite.DoSetupTest(b)
|
||||
suite.SetupTest()
|
||||
vmdb := suite.StateDB()
|
||||
|
||||
b.ResetTimer()
|
||||
@@ -177,7 +177,7 @@ func BenchmarkAddRefund(b *testing.B) {
|
||||
|
||||
func BenchmarkSuicide(b *testing.B) {
|
||||
suite := KeeperTestSuite{}
|
||||
suite.DoSetupTest(b)
|
||||
suite.SetupTest()
|
||||
vmdb := suite.StateDB()
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
authtx "github.com/cosmos/cosmos-sdk/x/auth/tx"
|
||||
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
|
||||
"github.com/cerc-io/laconicd/crypto/ethsecp256k1"
|
||||
"github.com/cerc-io/laconicd/tests"
|
||||
"github.com/cerc-io/laconicd/x/evm/statedb"
|
||||
"github.com/cerc-io/laconicd/x/evm/types"
|
||||
@@ -420,12 +421,26 @@ func (suite *KeeperTestSuite) TestSuicide() {
|
||||
suite.Require().NoError(db.Commit())
|
||||
db = suite.StateDB()
|
||||
|
||||
// Generate 2nd address
|
||||
privkey, _ := ethsecp256k1.GenerateKey()
|
||||
key, err := privkey.ToECDSA()
|
||||
suite.Require().NoError(err)
|
||||
addr2 := crypto.PubkeyToAddress(key.PublicKey)
|
||||
|
||||
// Add code and state to account 2
|
||||
db.SetCode(addr2, code)
|
||||
suite.Require().Equal(code, db.GetCode(addr2))
|
||||
for i := 0; i < 5; i++ {
|
||||
db.SetState(addr2, common.BytesToHash([]byte(fmt.Sprintf("key%d", i))), common.BytesToHash([]byte(fmt.Sprintf("value%d", i))))
|
||||
}
|
||||
|
||||
// Call Suicide
|
||||
suite.Require().Equal(true, db.Suicide(suite.address))
|
||||
|
||||
// Check suicided is marked
|
||||
suite.Require().Equal(true, db.HasSuicided(suite.address))
|
||||
|
||||
// Commit state
|
||||
suite.Require().NoError(db.Commit())
|
||||
db = suite.StateDB()
|
||||
|
||||
@@ -441,6 +456,10 @@ func (suite *KeeperTestSuite) TestSuicide() {
|
||||
|
||||
// Check account is deleted
|
||||
suite.Require().Equal(common.Hash{}, db.GetCodeHash(suite.address))
|
||||
|
||||
// Check code is still present in addr2 and suicided is false
|
||||
suite.Require().NotNil(db.GetCode(addr2))
|
||||
suite.Require().Equal(false, db.HasSuicided(addr2))
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) TestExist() {
|
||||
|
||||
+36
-22
@@ -1,12 +1,13 @@
|
||||
package keeper
|
||||
|
||||
import (
|
||||
"math"
|
||||
"math/big"
|
||||
|
||||
sdkmath "cosmossdk.io/math"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
|
||||
ante "github.com/cosmos/cosmos-sdk/x/auth/ante"
|
||||
authante "github.com/cosmos/cosmos-sdk/x/auth/ante"
|
||||
|
||||
evmtypes "github.com/cerc-io/laconicd/x/evm/types"
|
||||
|
||||
@@ -15,19 +16,20 @@ import (
|
||||
)
|
||||
|
||||
// DeductTxCostsFromUserBalance it calculates the tx costs and deducts the fees
|
||||
// returns (effectiveFee, priority, error)
|
||||
func (k Keeper) DeductTxCostsFromUserBalance(
|
||||
ctx sdk.Context,
|
||||
msgEthTx evmtypes.MsgEthereumTx,
|
||||
txData evmtypes.TxData,
|
||||
denom string,
|
||||
homestead, istanbul, london bool,
|
||||
) (sdk.Coins, error) {
|
||||
) (fees sdk.Coins, priority int64, err error) {
|
||||
isContractCreation := txData.GetTo() == nil
|
||||
|
||||
// fetch sender account from signature
|
||||
signerAcc, err := ante.GetSignerAcc(ctx, k.accountKeeper, msgEthTx.GetFrom())
|
||||
signerAcc, err := authante.GetSignerAcc(ctx, k.accountKeeper, msgEthTx.GetFrom())
|
||||
if err != nil {
|
||||
return nil, sdkerrors.Wrapf(err, "account not found for sender %s", msgEthTx.From)
|
||||
return nil, 0, sdkerrors.Wrapf(err, "account not found for sender %s", msgEthTx.From)
|
||||
}
|
||||
|
||||
gasLimit := txData.GetGas()
|
||||
@@ -39,7 +41,7 @@ func (k Keeper) DeductTxCostsFromUserBalance(
|
||||
|
||||
intrinsicGas, err := core.IntrinsicGas(txData.GetData(), accessList, isContractCreation, homestead, istanbul)
|
||||
if err != nil {
|
||||
return nil, sdkerrors.Wrapf(
|
||||
return nil, 0, sdkerrors.Wrapf(
|
||||
err,
|
||||
"failed to retrieve intrinsic gas, contract creation = %t; homestead = %t, istanbul = %t",
|
||||
isContractCreation, homestead, istanbul,
|
||||
@@ -48,7 +50,7 @@ func (k Keeper) DeductTxCostsFromUserBalance(
|
||||
|
||||
// intrinsic gas verification during CheckTx
|
||||
if ctx.IsCheckTx() && gasLimit < intrinsicGas {
|
||||
return nil, sdkerrors.Wrapf(
|
||||
return nil, 0, sdkerrors.Wrapf(
|
||||
sdkerrors.ErrOutOfGas,
|
||||
"gas limit too low: %d (gas limit) < %d (intrinsic gas)", gasLimit, intrinsicGas,
|
||||
)
|
||||
@@ -56,39 +58,51 @@ func (k Keeper) DeductTxCostsFromUserBalance(
|
||||
|
||||
var feeAmt *big.Int
|
||||
|
||||
feeMktParams := k.feeMarketKeeper.GetParams(ctx)
|
||||
if london && !feeMktParams.NoBaseFee && txData.TxType() == ethtypes.DynamicFeeTxType {
|
||||
baseFee := k.feeMarketKeeper.GetBaseFee(ctx)
|
||||
if txData.GetGasFeeCap().Cmp(baseFee) < 0 {
|
||||
return nil, sdkerrors.Wrapf(sdkerrors.ErrInsufficientFee, "the tx gasfeecap is lower than the tx baseFee: %s (gasfeecap), %s (basefee) ", txData.GetGasFeeCap(), baseFee)
|
||||
}
|
||||
feeAmt = txData.EffectiveFee(baseFee)
|
||||
} else {
|
||||
feeAmt = txData.Fee()
|
||||
baseFee := k.getBaseFee(ctx, london)
|
||||
if baseFee != nil && txData.GetGasFeeCap().Cmp(baseFee) < 0 {
|
||||
return nil, 0, sdkerrors.Wrapf(sdkerrors.ErrInsufficientFee,
|
||||
"the tx gasfeecap is lower than the tx baseFee: %s (gasfeecap), %s (basefee) ",
|
||||
txData.GetGasFeeCap(),
|
||||
baseFee)
|
||||
}
|
||||
|
||||
feeAmt = txData.EffectiveFee(baseFee)
|
||||
if feeAmt.Sign() == 0 {
|
||||
// zero fee, no need to deduct
|
||||
return sdk.NewCoins(), nil
|
||||
return sdk.Coins{}, 0, nil
|
||||
}
|
||||
|
||||
fees := sdk.Coins{sdk.NewCoin(denom, sdk.NewIntFromBigInt(feeAmt))}
|
||||
fees = sdk.Coins{sdk.NewCoin(denom, sdkmath.NewIntFromBigInt(feeAmt))}
|
||||
|
||||
// deduct the full gas cost from the user balance
|
||||
if err := ante.DeductFees(k.bankKeeper, ctx, signerAcc, fees); err != nil {
|
||||
return nil, sdkerrors.Wrapf(
|
||||
if err := authante.DeductFees(k.bankKeeper, ctx, signerAcc, fees); err != nil {
|
||||
return nil, 0, sdkerrors.Wrapf(
|
||||
err,
|
||||
"failed to deduct full gas cost %s from the user %s balance",
|
||||
fees, msgEthTx.From,
|
||||
)
|
||||
}
|
||||
return fees, nil
|
||||
|
||||
// calculate priority based on effective gas price
|
||||
tipPrice := txData.EffectiveGasPrice(baseFee)
|
||||
// if london hardfork is not enabled, tipPrice is the gasPrice
|
||||
if baseFee != nil {
|
||||
tipPrice = new(big.Int).Sub(tipPrice, baseFee)
|
||||
}
|
||||
priorityBig := new(big.Int).Quo(tipPrice, evmtypes.DefaultPriorityReduction.BigInt())
|
||||
if !priorityBig.IsInt64() {
|
||||
priority = math.MaxInt64
|
||||
} else {
|
||||
priority = priorityBig.Int64()
|
||||
}
|
||||
|
||||
return fees, priority, nil
|
||||
}
|
||||
|
||||
// CheckSenderBalance validates that the tx cost value is positive and that the
|
||||
// sender has enough funds to pay for the fees and value of the transaction.
|
||||
func CheckSenderBalance(
|
||||
balance sdk.Int,
|
||||
balance sdkmath.Int,
|
||||
txData evmtypes.TxData,
|
||||
) error {
|
||||
cost := txData.Cost()
|
||||
|
||||
+19
-17
@@ -3,6 +3,7 @@ package keeper_test
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
sdkmath "cosmossdk.io/math"
|
||||
evmkeeper "github.com/cerc-io/laconicd/x/evm/keeper"
|
||||
evmtypes "github.com/cerc-io/laconicd/x/evm/types"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
@@ -12,21 +13,21 @@ import (
|
||||
)
|
||||
|
||||
func (suite *KeeperTestSuite) TestCheckSenderBalance() {
|
||||
hundredInt := sdk.NewInt(100)
|
||||
hundredInt := sdkmath.NewInt(100)
|
||||
zeroInt := sdk.ZeroInt()
|
||||
oneInt := sdk.OneInt()
|
||||
fiveInt := sdk.NewInt(5)
|
||||
fiftyInt := sdk.NewInt(50)
|
||||
negInt := sdk.NewInt(-10)
|
||||
fiveInt := sdkmath.NewInt(5)
|
||||
fiftyInt := sdkmath.NewInt(50)
|
||||
negInt := sdkmath.NewInt(-10)
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
to string
|
||||
gasLimit uint64
|
||||
gasPrice *sdk.Int
|
||||
gasPrice *sdkmath.Int
|
||||
gasFeeCap *big.Int
|
||||
gasTipCap *big.Int
|
||||
cost *sdk.Int
|
||||
cost *sdkmath.Int
|
||||
from string
|
||||
accessList *ethtypes.AccessList
|
||||
expectPass bool
|
||||
@@ -237,7 +238,7 @@ func (suite *KeeperTestSuite) TestCheckSenderBalance() {
|
||||
|
||||
acct := suite.app.EvmKeeper.GetAccountOrEmpty(suite.ctx, suite.address)
|
||||
err := evmkeeper.CheckSenderBalance(
|
||||
sdk.NewIntFromBigInt(acct.Balance),
|
||||
sdkmath.NewIntFromBigInt(acct.Balance),
|
||||
txData,
|
||||
)
|
||||
|
||||
@@ -251,22 +252,22 @@ func (suite *KeeperTestSuite) TestCheckSenderBalance() {
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) TestDeductTxCostsFromUserBalance() {
|
||||
hundredInt := sdk.NewInt(100)
|
||||
hundredInt := sdkmath.NewInt(100)
|
||||
zeroInt := sdk.ZeroInt()
|
||||
oneInt := sdk.NewInt(1)
|
||||
fiveInt := sdk.NewInt(5)
|
||||
fiftyInt := sdk.NewInt(50)
|
||||
oneInt := sdkmath.NewInt(1)
|
||||
fiveInt := sdkmath.NewInt(5)
|
||||
fiftyInt := sdkmath.NewInt(50)
|
||||
|
||||
// should be enough to cover all test cases
|
||||
initBalance := sdk.NewInt((ethparams.InitialBaseFee + 10) * 105)
|
||||
initBalance := sdkmath.NewInt((ethparams.InitialBaseFee + 10) * 105)
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
gasLimit uint64
|
||||
gasPrice *sdk.Int
|
||||
gasPrice *sdkmath.Int
|
||||
gasFeeCap *big.Int
|
||||
gasTipCap *big.Int
|
||||
cost *sdk.Int
|
||||
cost *sdkmath.Int
|
||||
accessList *ethtypes.AccessList
|
||||
expectPass bool
|
||||
enableFeemarket bool
|
||||
@@ -402,7 +403,7 @@ func (suite *KeeperTestSuite) TestDeductTxCostsFromUserBalance() {
|
||||
|
||||
txData, _ := evmtypes.UnpackTxData(tx.Data)
|
||||
|
||||
fees, err := suite.app.EvmKeeper.DeductTxCostsFromUserBalance(
|
||||
fees, priority, err := suite.app.EvmKeeper.DeductTxCostsFromUserBalance(
|
||||
suite.ctx,
|
||||
*tx,
|
||||
txData,
|
||||
@@ -419,15 +420,16 @@ func (suite *KeeperTestSuite) TestDeductTxCostsFromUserBalance() {
|
||||
suite.Require().Equal(
|
||||
fees,
|
||||
sdk.NewCoins(
|
||||
sdk.NewCoin(evmtypes.DefaultEVMDenom, sdk.NewIntFromBigInt(txData.EffectiveFee(baseFee))),
|
||||
sdk.NewCoin(evmtypes.DefaultEVMDenom, sdkmath.NewIntFromBigInt(txData.EffectiveFee(baseFee))),
|
||||
),
|
||||
"valid test %d failed, fee value is wrong ", i,
|
||||
)
|
||||
suite.Require().Equal(int64(0), priority)
|
||||
} else {
|
||||
suite.Require().Equal(
|
||||
fees,
|
||||
sdk.NewCoins(
|
||||
sdk.NewCoin(evmtypes.DefaultEVMDenom, tc.gasPrice.Mul(sdk.NewIntFromUint64(tc.gasLimit))),
|
||||
sdk.NewCoin(evmtypes.DefaultEVMDenom, tc.gasPrice.Mul(sdkmath.NewIntFromUint64(tc.gasLimit))),
|
||||
),
|
||||
"valid test %d failed, fee value is wrong ", i,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user