Merge PR #3555: Reintroduce Fees OR Semantics
This commit is contained in:
parent
685bfca1d4
commit
7bc837aa06
|
@ -21,6 +21,7 @@ BREAKING CHANGES
|
|||
|
||||
* Gaia
|
||||
* [\#3457](https://github.com/cosmos/cosmos-sdk/issues/3457) Changed governance tally validatorGovInfo to use sdk.Int power instead of sdk.Dec
|
||||
* Reintroduce OR semantics for tx fees
|
||||
|
||||
* SDK
|
||||
* \#2513 Tendermint updates are adjusted by 10^-6 relative to staking tokens,
|
||||
|
|
|
@ -89,13 +89,13 @@ func TestGaiaCLIMinimumFees(t *testing.T) {
|
|||
tests.WaitForNextNBlocksTM(1, f.Port)
|
||||
|
||||
// Ensure tx w/ correct fees pass
|
||||
txFees := fmt.Sprintf("--fees=%s,%s", sdk.NewInt64Coin(feeDenom, 2), sdk.NewInt64Coin(fee2Denom, 2))
|
||||
txFees := fmt.Sprintf("--fees=%s", sdk.NewInt64Coin(feeDenom, 2))
|
||||
success, _, _ = f.TxSend(keyFoo, barAddr, sdk.NewInt64Coin(fee2Denom, 10), txFees)
|
||||
require.True(f.T, success)
|
||||
tests.WaitForNextNBlocksTM(1, f.Port)
|
||||
|
||||
// Ensure tx w/ improper fees fails
|
||||
txFees = fmt.Sprintf("--fees=%s", sdk.NewInt64Coin(feeDenom, 5))
|
||||
txFees = fmt.Sprintf("--fees=%s", sdk.NewInt64Coin(feeDenom, 1))
|
||||
success, _, _ = f.TxSend(keyFoo, barAddr, sdk.NewInt64Coin(fooDenom, 10), txFees)
|
||||
require.False(f.T, success)
|
||||
|
||||
|
|
|
@ -137,7 +137,7 @@ the transaction being included in the ledger.
|
|||
Validator's have a minimum gas price (multi-denom) configuration and they use
|
||||
this value when when determining if they should include the transaction in a block
|
||||
during `CheckTx`, where `gasPrices >= minGasPrices`. Note, your transaction must
|
||||
supply fees that match all the denominations the validator requires.
|
||||
supply fees that are greater than or equal to __any__ of the denominations the validator requires.
|
||||
|
||||
__Note__: With such a mechanism in place, validators may start to prioritize
|
||||
txs by `gasPrice` in the mempool, so providing higher fees or gas prices may yield
|
||||
|
|
|
@ -13,7 +13,7 @@ signature verification, as well as costs proportional to the tx size. Operators
|
|||
should set minimum gas prices when starting their nodes. They must set the unit
|
||||
costs of gas in each token denomination they wish to support:
|
||||
|
||||
`gaiad start ... --minimum-gas-prices=0.00001steak,0.05photinos`
|
||||
`gaiad start ... --minimum-gas-prices=0.00001steak;0.05photinos`
|
||||
|
||||
When adding transactions to mempool or gossipping transactions, validators check
|
||||
if the transaction's gas prices, which are determined by the provided fees, meet
|
||||
|
|
|
@ -13,8 +13,8 @@ const (
|
|||
// BaseConfig defines the server's basic configuration
|
||||
type BaseConfig struct {
|
||||
// The minimum gas prices a validator is willing to accept for processing a
|
||||
// transaction. A transaction's fees must meet the minimum of each denomination
|
||||
// specified in this config (e.g. 0.01photino,0.0001stake).
|
||||
// transaction. A transaction's fees must meet the minimum of any denomination
|
||||
// specified in this config (e.g. 0.01photino;0.0001stake).
|
||||
MinGasPrices string `mapstructure:"minimum-gas-prices"`
|
||||
}
|
||||
|
||||
|
|
|
@ -14,8 +14,8 @@ const defaultConfigTemplate = `# This is a TOML config file.
|
|||
##### main base config options #####
|
||||
|
||||
# The minimum gas prices a validator is willing to accept for processing a
|
||||
# transaction. A transaction's fees must meet the minimum of each denomination
|
||||
# specified in this config (e.g. 0.01photino,0.0001stake).
|
||||
# transaction. A transaction's fees must meet the minimum of any denomination
|
||||
# specified in this config (e.g. 0.01photino;0.0001stake).
|
||||
minimum-gas-prices = "{{ .BaseConfig.MinGasPrices }}"
|
||||
`
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ func StartCmd(ctx *Context, appCreator AppCreator) *cobra.Command {
|
|||
cmd.Flags().String(flagPruning, "syncable", "Pruning strategy: syncable, nothing, everything")
|
||||
cmd.Flags().String(
|
||||
FlagMinGasPrices, "",
|
||||
"Minimum gas prices to accept for transactions; All fees in a tx must meet this minimum (e.g. 0.01photino,0.0001stake)",
|
||||
"Minimum gas prices to accept for transactions; Any fee in a tx must meet this minimum (e.g. 0.01photino;0.0001stake)",
|
||||
)
|
||||
|
||||
// add support for all Tendermint-specific command line options
|
||||
|
|
|
@ -299,6 +299,26 @@ func (coins Coins) IsAllLTE(coinsB Coins) bool {
|
|||
return coinsB.IsAllGTE(coins)
|
||||
}
|
||||
|
||||
// IsAnyGTE returns true iff coins contains at least one denom that is present
|
||||
// at a greater or equal amount in coinsB; it returns false otherwise.
|
||||
//
|
||||
// NOTE: IsAnyGTE operates under the invariant that both coin sets are sorted
|
||||
// by denominations and there exists no zero coins.
|
||||
func (coins Coins) IsAnyGTE(coinsB Coins) bool {
|
||||
if len(coinsB) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, coin := range coins {
|
||||
amt := coinsB.AmountOf(coin.Denom)
|
||||
if coin.Amount.GTE(amt) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// IsZero returns true if there are no coins or all coins are zero.
|
||||
func (coins Coins) IsZero() bool {
|
||||
for _, coin := range coins {
|
||||
|
|
|
@ -508,3 +508,22 @@ func TestAmountOf(t *testing.T) {
|
|||
|
||||
assert.Panics(t, func() { cases[0].coins.AmountOf("Invalid") })
|
||||
}
|
||||
|
||||
func TestCoinsIsAnyGTE(t *testing.T) {
|
||||
one := NewInt(1)
|
||||
two := NewInt(2)
|
||||
|
||||
assert.False(t, Coins{}.IsAnyGTE(Coins{}))
|
||||
assert.False(t, Coins{{"a", one}}.IsAnyGTE(Coins{}))
|
||||
assert.False(t, Coins{}.IsAnyGTE(Coins{{"a", one}}))
|
||||
assert.False(t, Coins{{"a", one}}.IsAnyGTE(Coins{{"a", two}}))
|
||||
assert.True(t, Coins{{"a", one}, {"b", two}}.IsAnyGTE(Coins{{"a", two}, {"b", one}}))
|
||||
assert.True(t, Coins{{"a", one}}.IsAnyGTE(Coins{{"a", one}}))
|
||||
assert.True(t, Coins{{"a", two}}.IsAnyGTE(Coins{{"a", one}}))
|
||||
assert.True(t, Coins{{"a", one}}.IsAnyGTE(Coins{{"a", one}, {"b", two}}))
|
||||
assert.True(t, Coins{{"b", two}}.IsAnyGTE(Coins{{"a", one}, {"b", two}}))
|
||||
assert.False(t, Coins{{"b", one}}.IsAnyGTE(Coins{{"a", one}, {"b", two}}))
|
||||
assert.True(t, Coins{{"a", one}, {"b", two}}.IsAnyGTE(Coins{{"a", one}, {"b", one}}))
|
||||
assert.True(t, Coins{{"a", one}, {"b", one}}.IsAnyGTE(Coins{{"a", one}, {"b", two}}))
|
||||
assert.True(t, Coins{{"x", one}, {"y", one}}.IsAnyGTE(Coins{{"b", one}, {"c", one}, {"y", one}, {"z", one}}))
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package types
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
|
@ -375,7 +376,8 @@ func ParseDecCoins(coinsStr string) (coins DecCoins, err error) {
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
coinStrs := strings.Split(coinsStr, ",")
|
||||
splitRe := regexp.MustCompile(",|;")
|
||||
coinStrs := splitRe.Split(coinsStr, -1)
|
||||
for _, coinStr := range coinStrs {
|
||||
coin, err := ParseDecCoin(coinStr)
|
||||
if err != nil {
|
||||
|
|
|
@ -16,6 +16,8 @@ func equal(i *big.Int, i2 *big.Int) bool { return i.Cmp(i2) == 0 }
|
|||
|
||||
func gt(i *big.Int, i2 *big.Int) bool { return i.Cmp(i2) == 1 }
|
||||
|
||||
func gte(i *big.Int, i2 *big.Int) bool { return i.Cmp(i2) >= 0 }
|
||||
|
||||
func lt(i *big.Int, i2 *big.Int) bool { return i.Cmp(i2) == -1 }
|
||||
|
||||
func lte(i *big.Int, i2 *big.Int) bool { return i.Cmp(i2) <= 0 }
|
||||
|
@ -188,6 +190,12 @@ func (i Int) GT(i2 Int) bool {
|
|||
return gt(i.i, i2.i)
|
||||
}
|
||||
|
||||
// GTE returns true if receiver Int is greater than or equal to the parameter
|
||||
// Int.
|
||||
func (i Int) GTE(i2 Int) bool {
|
||||
return gte(i.i, i2.i)
|
||||
}
|
||||
|
||||
// LT returns true if first Int is lesser than second
|
||||
func (i Int) LT(i2 Int) bool {
|
||||
return lt(i.i, i2.i)
|
||||
|
|
|
@ -345,7 +345,7 @@ func EnsureSufficientMempoolFees(ctx sdk.Context, stdFee StdFee) sdk.Result {
|
|||
requiredFees[i] = sdk.NewCoin(gp.Denom, fee.Ceil().RoundInt())
|
||||
}
|
||||
|
||||
if !stdFee.Amount.IsAllGTE(requiredFees) {
|
||||
if !stdFee.Amount.IsAnyGTE(requiredFees) {
|
||||
return sdk.ErrInsufficientFee(
|
||||
fmt.Sprintf(
|
||||
"insufficient fees; got: %q required: %q", stdFee.Amount, requiredFees,
|
||||
|
|
|
@ -710,8 +710,8 @@ func TestEnsureSufficientMempoolFees(t *testing.T) {
|
|||
input := setupTestInput()
|
||||
ctx := input.ctx.WithMinGasPrices(
|
||||
sdk.DecCoins{
|
||||
sdk.NewDecCoinFromDec("photino", sdk.NewDecWithPrec(1000000, sdk.Precision)), // 0.0001photino
|
||||
sdk.NewDecCoinFromDec("stake", sdk.NewDecWithPrec(10000, sdk.Precision)), // 0.000001stake
|
||||
sdk.NewDecCoinFromDec("photino", sdk.NewDecWithPrec(50000000000000, sdk.Precision)), // 0.0001photino
|
||||
sdk.NewDecCoinFromDec("stake", sdk.NewDecWithPrec(10000000000000, sdk.Precision)), // 0.000001stake
|
||||
},
|
||||
)
|
||||
|
||||
|
@ -719,14 +719,16 @@ func TestEnsureSufficientMempoolFees(t *testing.T) {
|
|||
input StdFee
|
||||
expectedOK bool
|
||||
}{
|
||||
{NewStdFee(200000, sdk.Coins{sdk.NewInt64Coin("photino", 5)}), false},
|
||||
{NewStdFee(200000, sdk.Coins{sdk.NewInt64Coin("stake", 1)}), false},
|
||||
{NewStdFee(200000, sdk.Coins{sdk.NewInt64Coin("photino", 20)}), false},
|
||||
{NewStdFee(200000, sdk.Coins{sdk.NewInt64Coin("stake", 2)}), true},
|
||||
{NewStdFee(200000, sdk.Coins{sdk.NewInt64Coin("photino", 10)}), true},
|
||||
{
|
||||
NewStdFee(
|
||||
200000,
|
||||
sdk.Coins{
|
||||
sdk.NewInt64Coin("photino", 20),
|
||||
sdk.NewInt64Coin("stake", 1),
|
||||
sdk.NewInt64Coin("photino", 10),
|
||||
sdk.NewInt64Coin("stake", 2),
|
||||
},
|
||||
),
|
||||
true,
|
||||
|
@ -735,9 +737,9 @@ func TestEnsureSufficientMempoolFees(t *testing.T) {
|
|||
NewStdFee(
|
||||
200000,
|
||||
sdk.Coins{
|
||||
sdk.NewInt64Coin("atom", 2),
|
||||
sdk.NewInt64Coin("photino", 20),
|
||||
sdk.NewInt64Coin("stake", 1),
|
||||
sdk.NewInt64Coin("atom", 5),
|
||||
sdk.NewInt64Coin("photino", 10),
|
||||
sdk.NewInt64Coin("stake", 2),
|
||||
},
|
||||
),
|
||||
true,
|
||||
|
|
Loading…
Reference in New Issue