b1cd16e5bf
* Problem: feemarket's query cli has redundant height parameter Soluton: - remove the positional height parameter, since there's a flag already. Update CHANGELOG.md * Apply feemarket to native cosmos tx - add tx extension option for user to input tip price - apply feemarket's base fee to native tx comments and cleanup fallback to default sdk logic when london hardfork not enabled integration test cleanup feemarket query cli commands Update CHANGELOG.md update unit tests disable feemarket in simulation tests for now fix lint Update app/simulation_test.go fix python lint fix lint Update x/evm/types/extension_option.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> address review suggestions * fix unit tests * fix integration test * improve unit test coverage * fix go lint * refactor * fix integration test * fix simulation tests * fix go linter Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>
678 lines
24 KiB
Go
678 lines
24 KiB
Go
package keeper_test
|
|
|
|
import (
|
|
"encoding/json"
|
|
"math/big"
|
|
"strings"
|
|
|
|
sdkmath "cosmossdk.io/math"
|
|
. "github.com/onsi/ginkgo/v2"
|
|
. "github.com/onsi/gomega"
|
|
|
|
"github.com/cosmos/cosmos-sdk/baseapp"
|
|
"github.com/cosmos/cosmos-sdk/client/tx"
|
|
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
"github.com/cosmos/cosmos-sdk/types/tx/signing"
|
|
authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing"
|
|
authtx "github.com/cosmos/cosmos-sdk/x/auth/tx"
|
|
"github.com/ethereum/go-ethereum/common"
|
|
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
|
"github.com/evmos/ethermint/app"
|
|
"github.com/evmos/ethermint/crypto/ethsecp256k1"
|
|
"github.com/evmos/ethermint/encoding"
|
|
"github.com/evmos/ethermint/tests"
|
|
"github.com/evmos/ethermint/testutil"
|
|
"github.com/evmos/ethermint/x/feemarket/types"
|
|
|
|
"github.com/cosmos/cosmos-sdk/simapp"
|
|
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
|
|
evmtypes "github.com/evmos/ethermint/x/evm/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
|
|
msg banktypes.MsgSend
|
|
)
|
|
|
|
Describe("Performing Cosmos transactions", func() {
|
|
Context("with min-gas-prices (local) < MinGasPrices (feemarket param)", func() {
|
|
BeforeEach(func() {
|
|
privKey, msg = setupTestWithContext("1", sdk.NewDec(3), sdk.ZeroInt())
|
|
})
|
|
|
|
Context("during CheckTx", func() {
|
|
It("should reject transactions with gasPrice < MinGasPrices", func() {
|
|
gasPrice := sdkmath.NewInt(2)
|
|
res := checkTx(privKey, &gasPrice, &msg)
|
|
Expect(res.IsOK()).To(Equal(false), "transaction should have failed")
|
|
Expect(
|
|
strings.Contains(res.GetLog(),
|
|
"provided fee < minimum global fee"),
|
|
).To(BeTrue(), res.GetLog())
|
|
})
|
|
|
|
It("should accept transactions with gasPrice >= MinGasPrices", func() {
|
|
gasPrice := sdkmath.NewInt(3)
|
|
res := checkTx(privKey, &gasPrice, &msg)
|
|
Expect(res.IsOK()).To(Equal(true), "transaction should have succeeded", res.GetLog())
|
|
})
|
|
})
|
|
|
|
Context("during DeliverTx", func() {
|
|
It("should reject transactions with gasPrice < MinGasPrices", func() {
|
|
gasPrice := sdkmath.NewInt(2)
|
|
res := deliverTx(privKey, &gasPrice, &msg)
|
|
Expect(res.IsOK()).To(Equal(false), "transaction should have failed")
|
|
Expect(
|
|
strings.Contains(res.GetLog(),
|
|
"provided fee < minimum global fee"),
|
|
).To(BeTrue(), res.GetLog())
|
|
})
|
|
|
|
It("should accept transactions with gasPrice >= MinGasPrices", func() {
|
|
gasPrice := sdkmath.NewInt(3)
|
|
res := deliverTx(privKey, &gasPrice, &msg)
|
|
Expect(res.IsOK()).To(Equal(true), "transaction should have succeeded", res.GetLog())
|
|
})
|
|
})
|
|
})
|
|
|
|
Context("with min-gas-prices (local) == MinGasPrices (feemarket param)", func() {
|
|
BeforeEach(func() {
|
|
privKey, msg = setupTestWithContext("3", sdk.NewDec(3), sdk.ZeroInt())
|
|
})
|
|
|
|
Context("during CheckTx", func() {
|
|
It("should reject transactions with gasPrice < min-gas-prices", func() {
|
|
gasPrice := sdkmath.NewInt(2)
|
|
res := checkTx(privKey, &gasPrice, &msg)
|
|
Expect(res.IsOK()).To(Equal(false), "transaction should have failed")
|
|
Expect(
|
|
strings.Contains(res.GetLog(),
|
|
"insufficient fee"),
|
|
).To(BeTrue(), res.GetLog())
|
|
})
|
|
|
|
It("should accept transactions with gasPrice >= MinGasPrices", func() {
|
|
gasPrice := sdkmath.NewInt(3)
|
|
res := checkTx(privKey, &gasPrice, &msg)
|
|
Expect(res.IsOK()).To(Equal(true), "transaction should have succeeded", res.GetLog())
|
|
})
|
|
})
|
|
|
|
Context("during DeliverTx", func() {
|
|
It("should reject transactions with gasPrice < MinGasPrices", func() {
|
|
gasPrice := sdkmath.NewInt(2)
|
|
res := deliverTx(privKey, &gasPrice, &msg)
|
|
Expect(res.IsOK()).To(Equal(false), "transaction should have failed")
|
|
Expect(
|
|
strings.Contains(res.GetLog(),
|
|
"provided fee < minimum global fee"),
|
|
).To(BeTrue(), res.GetLog())
|
|
})
|
|
|
|
It("should accept transactions with gasPrice >= MinGasPrices", func() {
|
|
gasPrice := sdkmath.NewInt(3)
|
|
res := deliverTx(privKey, &gasPrice, &msg)
|
|
Expect(res.IsOK()).To(Equal(true), "transaction should have succeeded", res.GetLog())
|
|
})
|
|
})
|
|
})
|
|
|
|
Context("with MinGasPrices (feemarket param) < min-gas-prices (local)", func() {
|
|
BeforeEach(func() {
|
|
privKey, msg = setupTestWithContext("5", sdk.NewDec(3), sdk.NewInt(5))
|
|
})
|
|
Context("during CheckTx", func() {
|
|
It("should reject transactions with gasPrice < MinGasPrices", func() {
|
|
gasPrice := sdkmath.NewInt(2)
|
|
res := checkTx(privKey, &gasPrice, &msg)
|
|
Expect(res.IsOK()).To(Equal(false), "transaction should have failed")
|
|
Expect(
|
|
strings.Contains(res.GetLog(),
|
|
"insufficient fee"),
|
|
).To(BeTrue(), res.GetLog())
|
|
})
|
|
|
|
It("should reject transactions with MinGasPrices < gasPrice < baseFee", func() {
|
|
gasPrice := sdkmath.NewInt(4)
|
|
res := checkTx(privKey, &gasPrice, &msg)
|
|
Expect(res.IsOK()).To(Equal(false), "transaction should have failed")
|
|
Expect(
|
|
strings.Contains(res.GetLog(),
|
|
"insufficient fee"),
|
|
).To(BeTrue(), res.GetLog())
|
|
})
|
|
|
|
It("should accept transactions with gasPrice >= baseFee", func() {
|
|
gasPrice := sdkmath.NewInt(5)
|
|
res := checkTx(privKey, &gasPrice, &msg)
|
|
Expect(res.IsOK()).To(Equal(true), "transaction should have succeeded", res.GetLog())
|
|
})
|
|
})
|
|
|
|
Context("during DeliverTx", func() {
|
|
It("should reject transactions with gasPrice < MinGasPrices", func() {
|
|
gasPrice := sdkmath.NewInt(2)
|
|
res := deliverTx(privKey, &gasPrice, &msg)
|
|
Expect(res.IsOK()).To(Equal(false), "transaction should have failed")
|
|
Expect(
|
|
strings.Contains(res.GetLog(),
|
|
"provided fee < minimum global fee"),
|
|
).To(BeTrue(), res.GetLog())
|
|
})
|
|
|
|
It("should reject transactions with MinGasPrices < gasPrice < baseFee", func() {
|
|
gasPrice := sdkmath.NewInt(4)
|
|
res := checkTx(privKey, &gasPrice, &msg)
|
|
Expect(res.IsOK()).To(Equal(false), "transaction should have failed")
|
|
Expect(
|
|
strings.Contains(res.GetLog(),
|
|
"insufficient fee"),
|
|
).To(BeTrue(), res.GetLog())
|
|
})
|
|
It("should accept transactions with gasPrice >= baseFee", func() {
|
|
gasPrice := sdkmath.NewInt(5)
|
|
res := deliverTx(privKey, &gasPrice, &msg)
|
|
Expect(res.IsOK()).To(Equal(true), "transaction should have succeeded", res.GetLog())
|
|
})
|
|
})
|
|
})
|
|
})
|
|
|
|
Describe("Performing EVM transactions", func() {
|
|
type txParams struct {
|
|
gasPrice *big.Int
|
|
gasFeeCap *big.Int
|
|
gasTipCap *big.Int
|
|
accesses *ethtypes.AccessList
|
|
}
|
|
type getprices func() txParams
|
|
|
|
Context("with BaseFee (feemarket) < MinGasPrices (feemarket param)", func() {
|
|
var (
|
|
baseFee int64
|
|
minGasPrices int64
|
|
)
|
|
|
|
BeforeEach(func() {
|
|
baseFee = 10_000_000_000
|
|
minGasPrices = baseFee + 30_000_000_000
|
|
|
|
// Note that the tests run the same transactions with `gasLimit =
|
|
// 100000`. With the fee calculation `Fee = (baseFee + tip) * gasLimit`,
|
|
// a `minGasPrices = 40_000_000_000` results in `minGlobalFee =
|
|
// 4000000000000000`
|
|
privKey, _ = setupTestWithContext("1", sdk.NewDec(minGasPrices), sdkmath.NewInt(baseFee))
|
|
})
|
|
|
|
Context("during CheckTx", func() {
|
|
DescribeTable("should reject transactions with EffectivePrice < MinGasPrices",
|
|
func(malleate getprices) {
|
|
p := malleate()
|
|
to := tests.GenerateAddress()
|
|
msgEthereumTx := buildEthTx(privKey, &to, p.gasPrice, p.gasFeeCap, p.gasTipCap, p.accesses)
|
|
res := checkEthTx(privKey, msgEthereumTx)
|
|
Expect(res.IsOK()).To(Equal(false), "transaction should have failed")
|
|
Expect(
|
|
strings.Contains(res.GetLog(),
|
|
"provided fee < minimum global fee"),
|
|
).To(BeTrue(), res.GetLog())
|
|
},
|
|
Entry("legacy tx", func() txParams {
|
|
return txParams{big.NewInt(minGasPrices - 10_000_000_000), nil, nil, nil}
|
|
}),
|
|
Entry("dynamic tx with GasFeeCap < MinGasPrices, no gasTipCap", func() txParams {
|
|
return txParams{nil, big.NewInt(minGasPrices - 10_000_000_000), big.NewInt(0), ðtypes.AccessList{}}
|
|
}),
|
|
Entry("dynamic tx with GasFeeCap < MinGasPrices, max gasTipCap", func() txParams {
|
|
// Note that max priority fee per gas can't be higher than the max fee per gas (gasFeeCap), i.e. 30_000_000_000)
|
|
return txParams{nil, big.NewInt(minGasPrices - 10_000_000_000), big.NewInt(30_000_000_000), ðtypes.AccessList{}}
|
|
}),
|
|
Entry("dynamic tx with GasFeeCap > MinGasPrices, EffectivePrice < MinGasPrices", func() txParams {
|
|
return txParams{nil, big.NewInt(minGasPrices + 10_000_000_000), big.NewInt(0), ðtypes.AccessList{}}
|
|
}),
|
|
)
|
|
|
|
DescribeTable("should accept transactions with gasPrice >= MinGasPrices",
|
|
func(malleate getprices) {
|
|
p := malleate()
|
|
to := tests.GenerateAddress()
|
|
msgEthereumTx := buildEthTx(privKey, &to, 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{big.NewInt(minGasPrices), nil, nil, nil}
|
|
}),
|
|
// Note that this tx is not rejected on CheckTx, but not on DeliverTx,
|
|
// as the baseFee is set to minGasPrices during DeliverTx when baseFee
|
|
// < minGasPrices
|
|
Entry("dynamic tx with GasFeeCap > MinGasPrices, EffectivePrice > MinGasPrices", func() txParams {
|
|
return txParams{nil, big.NewInt(minGasPrices), big.NewInt(30_000_000_000), ðtypes.AccessList{}}
|
|
}),
|
|
)
|
|
})
|
|
|
|
Context("during DeliverTx", func() {
|
|
DescribeTable("should reject transactions with gasPrice < MinGasPrices",
|
|
func(malleate getprices) {
|
|
p := malleate()
|
|
to := tests.GenerateAddress()
|
|
msgEthereumTx := buildEthTx(privKey, &to, p.gasPrice, p.gasFeeCap, p.gasTipCap, p.accesses)
|
|
res := deliverEthTx(privKey, msgEthereumTx)
|
|
Expect(res.IsOK()).To(Equal(false), "transaction should have failed")
|
|
Expect(
|
|
strings.Contains(res.GetLog(),
|
|
"provided fee < minimum global fee"),
|
|
).To(BeTrue(), res.GetLog())
|
|
},
|
|
Entry("legacy tx", func() txParams {
|
|
return txParams{big.NewInt(minGasPrices - 10_000_000_000), nil, nil, nil}
|
|
}),
|
|
Entry("dynamic tx with GasFeeCap < MinGasPrices, no gasTipCap", func() txParams {
|
|
return txParams{nil, big.NewInt(minGasPrices - 10_000_000_000), big.NewInt(0), ðtypes.AccessList{}}
|
|
}),
|
|
Entry("dynamic tx with GasFeeCap < MinGasPrices, max gasTipCap", func() txParams {
|
|
// Note that max priority fee per gas can't be higher than the max fee per gas (gasFeeCap), i.e. 30_000_000_000)
|
|
return txParams{nil, big.NewInt(minGasPrices - 10_000_000_000), big.NewInt(30_000_000_000), ðtypes.AccessList{}}
|
|
}),
|
|
)
|
|
|
|
DescribeTable("should accept transactions with gasPrice >= MinGasPrices",
|
|
func(malleate getprices) {
|
|
p := malleate()
|
|
to := tests.GenerateAddress()
|
|
msgEthereumTx := buildEthTx(privKey, &to, 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{big.NewInt(minGasPrices + 1), nil, nil, nil}
|
|
}),
|
|
Entry("dynamic tx, EffectivePrice > MinGasPrices", func() txParams {
|
|
return txParams{nil, big.NewInt(minGasPrices + 10_000_000_000), big.NewInt(30_000_000_000), ðtypes.AccessList{}}
|
|
}),
|
|
)
|
|
})
|
|
})
|
|
|
|
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), sdkmath.NewInt(baseFee))
|
|
})
|
|
|
|
Context("during CheckTx", func() {
|
|
DescribeTable("should reject transactions with gasPrice < MinGasPrices",
|
|
func(malleate getprices) {
|
|
p := malleate()
|
|
to := tests.GenerateAddress()
|
|
msgEthereumTx := buildEthTx(privKey, &to, p.gasPrice, p.gasFeeCap, p.gasTipCap, p.accesses)
|
|
res := checkEthTx(privKey, msgEthereumTx)
|
|
Expect(res.IsOK()).To(Equal(false), "transaction should have failed")
|
|
Expect(
|
|
strings.Contains(res.GetLog(),
|
|
"provided fee < minimum global fee"),
|
|
).To(BeTrue(), res.GetLog())
|
|
},
|
|
Entry("legacy tx", func() txParams {
|
|
return txParams{big.NewInt(minGasPrices - 1_000_000_000), nil, nil, nil}
|
|
}),
|
|
Entry("dynamic tx with GasFeeCap < MinGasPrices, no gasTipCap", func() txParams {
|
|
return txParams{nil, big.NewInt(minGasPrices - 1_000_000_000), big.NewInt(0), ðtypes.AccessList{}}
|
|
}),
|
|
Entry("dynamic tx with GasFeeCap < MinGasPrices, max gasTipCap", func() txParams {
|
|
return txParams{nil, big.NewInt(minGasPrices - 1_000_000_000), big.NewInt(minGasPrices - 1_000_000_000), ðtypes.AccessList{}}
|
|
}),
|
|
)
|
|
|
|
DescribeTable("should reject transactions with MinGasPrices < tx gasPrice < EffectivePrice",
|
|
func(malleate getprices) {
|
|
p := malleate()
|
|
to := tests.GenerateAddress()
|
|
msgEthereumTx := buildEthTx(privKey, &to, p.gasPrice, p.gasFeeCap, p.gasTipCap, p.accesses)
|
|
res := checkEthTx(privKey, msgEthereumTx)
|
|
Expect(res.IsOK()).To(Equal(false), "transaction should have failed")
|
|
Expect(
|
|
strings.Contains(res.GetLog(),
|
|
"insufficient fee"),
|
|
).To(BeTrue(), res.GetLog())
|
|
},
|
|
Entry("legacy tx", func() txParams {
|
|
return txParams{big.NewInt(baseFee - 1_000_000_000), nil, nil, nil}
|
|
}),
|
|
Entry("dynamic tx", func() txParams {
|
|
return txParams{nil, big.NewInt(baseFee - 1_000_000_000), big.NewInt(0), ðtypes.AccessList{}}
|
|
}),
|
|
)
|
|
|
|
DescribeTable("should accept transactions with gasPrice >= EffectivePrice",
|
|
func(malleate getprices) {
|
|
p := malleate()
|
|
to := tests.GenerateAddress()
|
|
msgEthereumTx := buildEthTx(privKey, &to, 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{big.NewInt(baseFee), nil, nil, nil}
|
|
}),
|
|
Entry("dynamic tx", func() txParams {
|
|
return txParams{nil, big.NewInt(baseFee), big.NewInt(0), ðtypes.AccessList{}}
|
|
}),
|
|
)
|
|
})
|
|
|
|
Context("during DeliverTx", func() {
|
|
DescribeTable("should reject transactions with gasPrice < MinGasPrices",
|
|
func(malleate getprices) {
|
|
p := malleate()
|
|
to := tests.GenerateAddress()
|
|
msgEthereumTx := buildEthTx(privKey, &to, p.gasPrice, p.gasFeeCap, p.gasTipCap, p.accesses)
|
|
res := deliverEthTx(privKey, msgEthereumTx)
|
|
Expect(res.IsOK()).To(Equal(false), "transaction should have failed")
|
|
Expect(
|
|
strings.Contains(res.GetLog(),
|
|
"provided fee < minimum global fee"),
|
|
).To(BeTrue(), res.GetLog())
|
|
},
|
|
Entry("legacy tx", func() txParams {
|
|
return txParams{big.NewInt(minGasPrices - 1_000_000_000), nil, nil, nil}
|
|
}),
|
|
Entry("dynamic tx", func() txParams {
|
|
return txParams{nil, big.NewInt(minGasPrices - 1_000_000_000), nil, ðtypes.AccessList{}}
|
|
}),
|
|
)
|
|
|
|
DescribeTable("should reject transactions with MinGasPrices < gasPrice < EffectivePrice",
|
|
func(malleate getprices) {
|
|
p := malleate()
|
|
to := tests.GenerateAddress()
|
|
msgEthereumTx := buildEthTx(privKey, &to, p.gasPrice, p.gasFeeCap, p.gasTipCap, p.accesses)
|
|
res := deliverEthTx(privKey, msgEthereumTx)
|
|
Expect(res.IsOK()).To(Equal(false), "transaction should have failed")
|
|
Expect(
|
|
strings.Contains(res.GetLog(),
|
|
"insufficient fee"),
|
|
).To(BeTrue(), res.GetLog())
|
|
},
|
|
// Note that the baseFee is not 10_000_000_000 anymore but updates to 8_750_000_000 because of the s.Commit
|
|
Entry("legacy tx", func() txParams {
|
|
return txParams{big.NewInt(baseFee - 2_000_000_000), nil, nil, nil}
|
|
}),
|
|
Entry("dynamic tx", func() txParams {
|
|
return txParams{nil, big.NewInt(baseFee - 2_000_000_000), big.NewInt(0), ðtypes.AccessList{}}
|
|
}),
|
|
)
|
|
|
|
DescribeTable("should accept transactions with gasPrice >= EffectivePrice",
|
|
func(malleate getprices) {
|
|
p := malleate()
|
|
to := tests.GenerateAddress()
|
|
msgEthereumTx := buildEthTx(privKey, &to, 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{big.NewInt(baseFee), nil, nil, nil}
|
|
}),
|
|
Entry("dynamic tx", func() txParams {
|
|
return txParams{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 sdkmath.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 := sdkmath.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: sdkmath.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,
|
|
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)
|
|
gasLimit := uint64(100000)
|
|
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)
|
|
|
|
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: sdkmath.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
|
|
}
|
|
|
|
func prepareCosmosTx(priv *ethsecp256k1.PrivKey, gasPrice *sdkmath.Int, msgs ...sdk.Msg) []byte {
|
|
encodingConfig := encoding.MakeConfig(app.ModuleBasics)
|
|
accountAddress := sdk.AccAddress(priv.PubKey().Address().Bytes())
|
|
|
|
txBuilder := encodingConfig.TxConfig.NewTxBuilder()
|
|
|
|
txBuilder.SetGasLimit(1000000)
|
|
if gasPrice == nil {
|
|
_gasPrice := sdkmath.NewInt(1)
|
|
gasPrice = &_gasPrice
|
|
}
|
|
fees := &sdk.Coins{{Denom: s.denom, Amount: gasPrice.MulRaw(1000000)}}
|
|
txBuilder.SetFeeAmount(*fees)
|
|
err := txBuilder.SetMsgs(msgs...)
|
|
s.Require().NoError(err)
|
|
|
|
seq, err := s.app.AccountKeeper.GetSequence(s.ctx, accountAddress)
|
|
s.Require().NoError(err)
|
|
|
|
// First round: we gather all the signer infos. We use the "set empty
|
|
// signature" hack to do that.
|
|
sigV2 := signing.SignatureV2{
|
|
PubKey: priv.PubKey(),
|
|
Data: &signing.SingleSignatureData{
|
|
SignMode: encodingConfig.TxConfig.SignModeHandler().DefaultMode(),
|
|
Signature: nil,
|
|
},
|
|
Sequence: seq,
|
|
}
|
|
|
|
sigsV2 := []signing.SignatureV2{sigV2}
|
|
|
|
err = txBuilder.SetSignatures(sigsV2...)
|
|
s.Require().NoError(err)
|
|
|
|
// Second round: all signer infos are set, so each signer can sign.
|
|
accNumber := s.app.AccountKeeper.GetAccount(s.ctx, accountAddress).GetAccountNumber()
|
|
signerData := authsigning.SignerData{
|
|
ChainID: s.ctx.ChainID(),
|
|
AccountNumber: accNumber,
|
|
Sequence: seq,
|
|
}
|
|
sigV2, err = tx.SignWithPrivKey(
|
|
encodingConfig.TxConfig.SignModeHandler().DefaultMode(), signerData,
|
|
txBuilder, priv, encodingConfig.TxConfig,
|
|
seq,
|
|
)
|
|
s.Require().NoError(err)
|
|
|
|
sigsV2 = []signing.SignatureV2{sigV2}
|
|
err = txBuilder.SetSignatures(sigsV2...)
|
|
s.Require().NoError(err)
|
|
|
|
// bz are bytes to be broadcasted over the network
|
|
bz, err := encodingConfig.TxConfig.TxEncoder()(txBuilder.GetTx())
|
|
s.Require().NoError(err)
|
|
return bz
|
|
}
|
|
|
|
func checkTx(priv *ethsecp256k1.PrivKey, gasPrice *sdkmath.Int, msgs ...sdk.Msg) abci.ResponseCheckTx {
|
|
bz := prepareCosmosTx(priv, gasPrice, msgs...)
|
|
req := abci.RequestCheckTx{Tx: bz}
|
|
res := s.app.BaseApp.CheckTx(req)
|
|
return res
|
|
}
|
|
|
|
func deliverTx(priv *ethsecp256k1.PrivKey, gasPrice *sdkmath.Int, msgs ...sdk.Msg) abci.ResponseDeliverTx {
|
|
bz := prepareCosmosTx(priv, gasPrice, msgs...)
|
|
req := abci.RequestDeliverTx{Tx: bz}
|
|
res := s.app.BaseApp.DeliverTx(req)
|
|
return res
|
|
}
|