Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

R4R: Refactor Gas/Fee Model #3258

Merged
merged 58 commits into from
Jan 18, 2019
Merged
Show file tree
Hide file tree
Changes from 51 commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
57e6d29
Remove initial minimum fee logic in favor of min gas prices
Jan 8, 2019
0c66cbd
Merge branch 'develop' into bez/3248-fee-model-refactor-i
Jan 8, 2019
89243a1
Remove dead ante code
Jan 8, 2019
4b60963
Fix IsAnyGTE and remove unused IsAnyGT
Jan 9, 2019
5d88b4e
Fix IsAnyGTE and remove unused IsAnyGT
Jan 9, 2019
3a4bb2e
Remove redundant coins length check
Jan 9, 2019
db72ffb
Add comment for searchOther purpose
Jan 9, 2019
6bda9b1
Fix IsAnyGTE godoc and add additional test case
Jan 9, 2019
7308a1f
Update EnsureSufficientMempoolFees and add unit tests
Jan 9, 2019
85bc046
Update context test
Jan 9, 2019
7ac8897
Merge branch 'bez/3262-fix-IsAnyGTE' into bez/3248-fee-model-refactor-i
Jan 9, 2019
674a3ca
Fix local net flags
Jan 9, 2019
861297c
Update config unit tests
Jan 9, 2019
3eaf90c
Merge branch 'develop' into bez/3248-fee-model-refactor-i
Jan 9, 2019
de87c75
Fix broken CLI tests
Jan 10, 2019
57dd18e
Change flag to "minimum_gas_prices" to support config file loading
Jan 10, 2019
dc988e6
Support manually providing gas prices via the CLI
Jan 10, 2019
81e9421
Support gas prices via REST
Jan 10, 2019
6104bac
Add gas_prices field to base_req in Swagger config
Jan 10, 2019
27bae4b
Implement TestGaiaCLIGasPrices
Jan 10, 2019
d52c5ee
Add a Fees & Gas section to gaia CLI docs
Jan 10, 2019
cc89443
Add pending log entry
Jan 10, 2019
a1e4679
Merge branch 'develop' into bez/3248-fee-model-refactor-i
Jan 10, 2019
ba0eb5a
Fix docs
Jan 10, 2019
a3e2307
Remove normalization gas factor
Jan 10, 2019
cd432b6
Fix unit tests due to removal of gas norm.
Jan 10, 2019
cabbce5
Update CLI tests to account for higher fee amounts
Jan 10, 2019
94c8860
Increase coin amounts in local testnet
Jan 10, 2019
1338072
Increase self-bond amount in local testnet
Jan 10, 2019
ccfac6e
Update FlagGasPrices description
Jan 11, 2019
43b0303
Merge branch 'develop' into bez/3248-fee-model-refactor-i
Jan 14, 2019
a2e2b79
Merge branch 'develop' into bez/3248-fee-model-refactor-i
Jan 17, 2019
12cbe4d
[wip] update minimum fees to be of type DecCoins
Jan 17, 2019
e31665a
implement Dec#Ceil along with unit tests
Jan 17, 2019
68c2004
add amount and denom checks to DecCoin constructors
Jan 17, 2019
f84ff9b
update DecCoin/s unit tests
Jan 17, 2019
02229e8
refactor EnsureSufficientMempoolFees
Jan 17, 2019
4fabff2
update CLI flag examples
Jan 17, 2019
5c9ac8b
implement stringer interfaces for DecCoin/s and associated unit tests
Jan 17, 2019
be7f097
Update server config to accept DecCoins
Jan 17, 2019
d3eece2
update ante handler godoc
Jan 17, 2019
e8341b8
rever local testnet account and bonding sums and update min gas prices
Jan 17, 2019
d4573c8
fix gaia imports
Jan 17, 2019
995a296
update TxBuilder to handle DecCoins for gas prices
Jan 17, 2019
81c06f4
update rest utils to use DecCoins for gas prices
Jan 17, 2019
d0c3def
update more cli and config examples
Jan 17, 2019
9fe90ea
Update Swagger
Jan 17, 2019
2404649
fix CLI tests
Jan 17, 2019
475d7ee
update gaiacli doc
Jan 17, 2019
12e4ad2
Merge branch 'develop' into bez/3248-fee-model-refactor-i
Jan 17, 2019
66c5b62
Update docs/gaia/gaiacli.md
jackzampolin Jan 17, 2019
bbb3f22
fix msgUndelegateInput
Jan 17, 2019
a3ebb34
Merge branch 'bez/3248-fee-model-refactor-i' of github.com:cosmos/cos…
Jan 17, 2019
eccdbfd
update fees & gas section
Jan 17, 2019
bece824
fix types unit tests to account for new precision
Jan 17, 2019
686a9b6
fix swagger def
Jan 17, 2019
27b1bff
Update baseapp/options.go
fedekunze Jan 18, 2019
b33ee79
Update client/utils/rest.go
fedekunze Jan 18, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions PENDING.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,11 @@ IMPROVEMENTS
* [\#3158](https://github.com/cosmos/cosmos-sdk/pull/3158) Validate slashing genesis
* [\#3172](https://github.com/cosmos/cosmos-sdk/pull/3172) Support minimum fees in a local testnet.
* [\#3250](https://github.com/cosmos/cosmos-sdk/pull/3250) Refactor integration tests and increase coverage
* [\#3248](https://github.com/cosmos/cosmos-sdk/issues/3248) Refactor tx fee
model:
* Validators specify minimum gas prices instead of minimum fees
* Clients may provide either fees or gas prices directly
* The gas prices of a tx must meet a validator's minimum
* [\#2859](https://github.com/cosmos/cosmos-sdk/issues/2859) Rename `TallyResult` in gov proposals to `FinalTallyResult`
* [\#3286](https://github.com/cosmos/cosmos-sdk/pull/3286) Fix `gaiad gentx` printout of account's addresses, i.e. user bech32 instead of hex.

Expand Down
20 changes: 13 additions & 7 deletions baseapp/baseapp.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,9 @@ type BaseApp struct {
// TODO move this in the future to baseapp param store on main store.
consensusParams *abci.ConsensusParams

// spam prevention
minimumFees sdk.Coins
// The minimum gas prices a validator is willing to accept for processing a
// transaction. This is mainly used for DoS and spam prevention.
minGasPrices sdk.DecCoins

// flag for sealing
sealed bool
Expand Down Expand Up @@ -213,13 +214,17 @@ func (app *BaseApp) initFromMainStore(mainKey *sdk.KVStoreKey) error {
return nil
}

func (app *BaseApp) setMinimumFees(fees sdk.Coins) { app.minimumFees = fees }
func (app *BaseApp) setMinGasPrices(gasPrices sdk.DecCoins) {
app.minGasPrices = gasPrices
}

// NewContext returns a new Context with the correct store, the given header, and nil txBytes.
func (app *BaseApp) NewContext(isCheckTx bool, header abci.Header) sdk.Context {
if isCheckTx {
return sdk.NewContext(app.checkState.ms, header, true, app.Logger).WithMinimumFees(app.minimumFees)
return sdk.NewContext(app.checkState.ms, header, true, app.Logger).
WithMinGasPrices(app.minGasPrices)
}

return sdk.NewContext(app.deliverState.ms, header, false, app.Logger)
}

Expand All @@ -240,7 +245,7 @@ func (app *BaseApp) setCheckState(header abci.Header) {
ms := app.cms.CacheMultiStore()
app.checkState = &state{
ms: ms,
ctx: sdk.NewContext(ms, header, true, app.Logger).WithMinimumFees(app.minimumFees),
ctx: sdk.NewContext(ms, header, true, app.Logger).WithMinGasPrices(app.minGasPrices),
}
}

Expand Down Expand Up @@ -455,8 +460,9 @@ func handleQueryCustom(app *BaseApp, path []string, req abci.RequestQuery) (res
}

// Cache wrap the commit-multistore for safety.
ctx := sdk.NewContext(app.cms.CacheMultiStore(), app.checkState.ctx.BlockHeader(), true, app.Logger).
WithMinimumFees(app.minimumFees)
ctx := sdk.NewContext(
app.cms.CacheMultiStore(), app.checkState.ctx.BlockHeader(), true, app.Logger,
).WithMinGasPrices(app.minGasPrices)

// Passes the rest of the path as an argument to the querier.
// For example, in the path "custom/gov/proposal/test", the gov querier gets []string{"proposal", "test"} as the path
Expand Down
9 changes: 5 additions & 4 deletions baseapp/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,13 @@ func SetPruning(opts sdk.PruningOptions) func(*BaseApp) {
}

// SetMinimumFees returns an option that sets the minimum fees on the app.
alexanderbez marked this conversation as resolved.
Show resolved Hide resolved
func SetMinimumFees(minFees string) func(*BaseApp) {
fees, err := sdk.ParseCoins(minFees)
func SetMinGasPrices(gasPricesStr string) func(*BaseApp) {
gasPrices, err := sdk.ParseDecCoins(gasPricesStr)
if err != nil {
panic(fmt.Sprintf("invalid minimum fees: %v", err))
panic(fmt.Sprintf("invalid minimum gas prices: %v", err))
}
return func(bap *BaseApp) { bap.setMinimumFees(fees) }

return func(bap *BaseApp) { bap.setMinGasPrices(gasPrices) }
}

func (app *BaseApp) SetName(name string) {
Expand Down
2 changes: 2 additions & 0 deletions client/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ const (
FlagSequence = "sequence"
FlagMemo = "memo"
FlagFees = "fees"
FlagGasPrices = "gas-prices"
FlagAsync = "async"
FlagJson = "json"
FlagPrintResponse = "print-response"
Expand Down Expand Up @@ -79,6 +80,7 @@ func PostCommands(cmds ...*cobra.Command) []*cobra.Command {
c.Flags().Uint64(FlagSequence, 0, "Sequence number to sign the tx")
c.Flags().String(FlagMemo, "", "Memo to send along with transaction")
c.Flags().String(FlagFees, "", "Fees to pay along with transaction; eg: 10stake,1atom")
c.Flags().String(FlagGasPrices, "", "Gas prices to determine the transaction fee (e.g. 0.00001stake)")
c.Flags().String(FlagNode, "tcp://localhost:26657", "<host>:<port> to tendermint rpc interface for this chain")
c.Flags().Bool(FlagUseLedger, false, "Use a connected Ledger device")
c.Flags().Float64(FlagGasAdjustment, DefaultGasAdjustment, "adjustment factor to be multiplied against the estimate returned by the tx simulation; if the gas limit is set manually this flag is ignored ")
Expand Down
3 changes: 2 additions & 1 deletion client/lcd/lcd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,8 @@ func TestCoinSendGenerateSignAndBroadcast(t *testing.T) {
payload := authrest.SignBody{
Tx: msg,
BaseReq: utils.NewBaseReq(
name1, pw, "", viper.GetString(client.FlagChainID), "", "", accnum, sequence, nil, false, false,
name1, pw, "", viper.GetString(client.FlagChainID), "", "",
accnum, sequence, nil, nil, false, false,
),
}
json, err := cdc.MarshalJSON(payload)
Expand Down
4 changes: 4 additions & 0 deletions client/lcd/swagger-ui/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2238,6 +2238,10 @@ definitions:
type: array
items:
$ref: "#/definitions/Coin"
gas_prices:
type: array
items:
$ref: "#/definitions/DecCoins"
jackzampolin marked this conversation as resolved.
Show resolved Hide resolved
generate_only:
type: boolean
example: false
Expand Down
23 changes: 12 additions & 11 deletions client/lcd/test_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -647,7 +647,7 @@ func doSign(t *testing.T, port, name, password, chainID string, accnum, sequence
payload := authrest.SignBody{
Tx: msg,
BaseReq: utils.NewBaseReq(
name, password, "", chainID, "", "", accnum, sequence, nil, false, false,
name, password, "", chainID, "", "", accnum, sequence, nil, nil, false, false,
),
}
json, err := cdc.MarshalJSON(payload)
Expand Down Expand Up @@ -703,8 +703,9 @@ func doTransferWithGas(t *testing.T, port, seed, name, memo, password string, ad
sequence := acc.GetSequence()
chainID := viper.GetString(client.FlagChainID)

baseReq := utils.NewBaseReq(name, password, memo, chainID, gas,
fmt.Sprintf("%f", gasAdjustment), accnum, sequence, fees,
baseReq := utils.NewBaseReq(
name, password, memo, chainID, gas,
fmt.Sprintf("%f", gasAdjustment), accnum, sequence, fees, nil,
generateOnly, simulate,
)

Expand Down Expand Up @@ -736,7 +737,7 @@ func doDelegate(t *testing.T, port, name, password string,
accnum := acc.GetAccountNumber()
sequence := acc.GetSequence()
chainID := viper.GetString(client.FlagChainID)
baseReq := utils.NewBaseReq(name, password, "", chainID, "", "", accnum, sequence, fees, false, false)
baseReq := utils.NewBaseReq(name, password, "", chainID, "", "", accnum, sequence, fees, nil, false, false)
msg := msgDelegationsInput{
BaseReq: baseReq,
DelegatorAddr: delAddr,
Expand Down Expand Up @@ -770,8 +771,8 @@ func doUndelegate(t *testing.T, port, name, password string,
accnum := acc.GetAccountNumber()
sequence := acc.GetSequence()
chainID := viper.GetString(client.FlagChainID)
baseReq := utils.NewBaseReq(name, password, "", chainID, "", "", accnum, sequence, fees, false, false)
msg := msgUndelegateInput{
baseReq := utils.NewBaseReq(name, password, "", chainID, "", "", accnum, sequence, fees, nil, false, false)
msg := msgBeginUnbondingInput{
BaseReq: baseReq,
DelegatorAddr: delAddr,
ValidatorAddr: valAddr,
Expand Down Expand Up @@ -806,7 +807,7 @@ func doBeginRedelegation(t *testing.T, port, name, password string,
sequence := acc.GetSequence()

chainID := viper.GetString(client.FlagChainID)
baseReq := utils.NewBaseReq(name, password, "", chainID, "", "", accnum, sequence, fees, false, false)
baseReq := utils.NewBaseReq(name, password, "", chainID, "", "", accnum, sequence, fees, nil, false, false)

msg := msgBeginRedelegateInput{
BaseReq: baseReq,
Expand Down Expand Up @@ -1037,7 +1038,7 @@ func doSubmitProposal(t *testing.T, port, seed, name, password string, proposerA
accnum := acc.GetAccountNumber()
sequence := acc.GetSequence()
chainID := viper.GetString(client.FlagChainID)
baseReq := utils.NewBaseReq(name, password, "", chainID, "", "", accnum, sequence, fees, false, false)
baseReq := utils.NewBaseReq(name, password, "", chainID, "", "", accnum, sequence, fees, nil, false, false)

pr := postProposalReq{
Title: "Test",
Expand Down Expand Up @@ -1133,7 +1134,7 @@ func doDeposit(t *testing.T, port, seed, name, password string, proposerAddr sdk
accnum := acc.GetAccountNumber()
sequence := acc.GetSequence()
chainID := viper.GetString(client.FlagChainID)
baseReq := utils.NewBaseReq(name, password, "", chainID, "", "", accnum, sequence, fees, false, false)
baseReq := utils.NewBaseReq(name, password, "", chainID, "", "", accnum, sequence, fees, nil, false, false)

dr := depositReq{
Depositor: proposerAddr,
Expand Down Expand Up @@ -1187,7 +1188,7 @@ func doVote(t *testing.T, port, seed, name, password string, proposerAddr sdk.Ac
accnum := acc.GetAccountNumber()
sequence := acc.GetSequence()
chainID := viper.GetString(client.FlagChainID)
baseReq := utils.NewBaseReq(name, password, "", chainID, "", "", accnum, sequence, fees, false, false)
baseReq := utils.NewBaseReq(name, password, "", chainID, "", "", accnum, sequence, fees, nil, false, false)

vr := voteReq{
Voter: proposerAddr,
Expand Down Expand Up @@ -1319,7 +1320,7 @@ func getSigningInfo(t *testing.T, port string, validatorPubKey string) slashing.
func doUnjail(t *testing.T, port, seed, name, password string,
valAddr sdk.ValAddress, fees sdk.Coins) (resultTx ctypes.ResultBroadcastTxCommit) {
chainID := viper.GetString(client.FlagChainID)
baseReq := utils.NewBaseReq(name, password, "", chainID, "", "", 1, 1, fees, false, false)
baseReq := utils.NewBaseReq(name, password, "", chainID, "", "", 1, 1, fees, nil, false, false)

ur := unjailReq{
BaseReq: baseReq,
Expand Down
60 changes: 39 additions & 21 deletions client/utils/rest.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,30 +101,33 @@ func WriteGenerateStdTxResponse(w http.ResponseWriter, cdc *codec.Codec, txBldr
// BaseReq defines a structure that can be embedded in other request structures
// that all share common "base" fields.
type BaseReq struct {
Name string `json:"name"`
Password string `json:"password"`
Memo string `json:"memo"`
ChainID string `json:"chain_id"`
AccountNumber uint64 `json:"account_number"`
Sequence uint64 `json:"sequence"`
Fees sdk.Coins `json:"fees"`
Gas string `json:"gas"`
GasAdjustment string `json:"gas_adjustment"`
GenerateOnly bool `json:"generate_only"`
Simulate bool `json:"simulate"`
Name string `json:"name"`
Password string `json:"password"`
Memo string `json:"memo"`
ChainID string `json:"chain_id"`
AccountNumber uint64 `json:"account_number"`
Sequence uint64 `json:"sequence"`
Fees sdk.Coins `json:"fees"`
GasPrices sdk.DecCoins `json:"gas_prices"`
Gas string `json:"gas"`
alexanderbez marked this conversation as resolved.
Show resolved Hide resolved
GasAdjustment string `json:"gas_adjustment"`
GenerateOnly bool `json:"generate_only"`
Simulate bool `json:"simulate"`
}

// NewBaseReq creates a new basic request instance and sanitizes its values
func NewBaseReq(
name, password, memo, chainID string, gas, gasAdjustment string,
accNumber, seq uint64, fees sdk.Coins, genOnly, simulate bool) BaseReq {
accNumber, seq uint64, fees sdk.Coins, gasPrices sdk.DecCoins, genOnly, simulate bool,
) BaseReq {

return BaseReq{
Name: strings.TrimSpace(name),
Password: password,
Memo: strings.TrimSpace(memo),
ChainID: strings.TrimSpace(chainID),
Fees: fees,
GasPrices: gasPrices,
Gas: strings.TrimSpace(gas),
GasAdjustment: strings.TrimSpace(gasAdjustment),
AccountNumber: accNumber,
Expand All @@ -136,11 +139,10 @@ func NewBaseReq(

// Sanitize performs basic sanitization on a BaseReq object.
func (br BaseReq) Sanitize() BaseReq {
newBr := NewBaseReq(
return NewBaseReq(
br.Name, br.Password, br.Memo, br.ChainID, br.Gas, br.GasAdjustment,
br.AccountNumber, br.Sequence, br.Fees, br.GenerateOnly, br.Simulate,
br.AccountNumber, br.Sequence, br.Fees, br.GasPrices, br.GenerateOnly, br.Simulate,
)
return newBr
}

// ValidateBasic performs basic validation of a BaseReq. If custom validation
Expand All @@ -152,18 +154,28 @@ func (br BaseReq) ValidateBasic(w http.ResponseWriter) bool {
case len(br.Password) == 0:
WriteErrorResponse(w, http.StatusUnauthorized, "password required but not specified")
return false

case len(br.ChainID) == 0:
WriteErrorResponse(w, http.StatusUnauthorized, "chain-id required but not specified")
return false
case !br.Fees.IsValid():
WriteErrorResponse(w, http.StatusPaymentRequired, "invalid or insufficient fees")

case !br.Fees.IsZero() && !br.GasPrices.IsZero():
// both fees and gas prices were provided
WriteErrorResponse(w, http.StatusPaymentRequired, "cannot provide both fees and gas prices")
alexanderbez marked this conversation as resolved.
Show resolved Hide resolved
return false

case !br.Fees.IsValid() && !br.GasPrices.IsValid():
// neither fees or gas prices were provided
WriteErrorResponse(w, http.StatusPaymentRequired, "invalid fees or gas prices provided")
return false
}
}

if len(br.Name) == 0 {
WriteErrorResponse(w, http.StatusUnauthorized, "name required but not specified")
return false
}

return true
}

Expand Down Expand Up @@ -203,8 +215,12 @@ func ReadRESTReq(w http.ResponseWriter, r *http.Request, cdc *codec.Codec, req i
// supplied messages. Finally, it broadcasts the signed transaction to a node.
//
// NOTE: Also see CompleteAndBroadcastTxCli.
// NOTE: Also see x/staking/client/rest/tx.go delegationsRequestHandlerFn.
func CompleteAndBroadcastTxREST(w http.ResponseWriter, r *http.Request, cliCtx context.CLIContext, baseReq BaseReq, msgs []sdk.Msg, cdc *codec.Codec) {
// NOTE: Also see x/stake/client/rest/tx.go delegationsRequestHandlerFn.
func CompleteAndBroadcastTxREST(
w http.ResponseWriter, r *http.Request, cliCtx context.CLIContext,
baseReq BaseReq, msgs []sdk.Msg, cdc *codec.Codec,
) {

gasAdjustment, ok := ParseFloat64OrReturnBadRequest(w, baseReq.GasAdjustment, client.DefaultGasAdjustment)
if !ok {
return
Expand All @@ -216,9 +232,11 @@ func CompleteAndBroadcastTxREST(w http.ResponseWriter, r *http.Request, cliCtx c
return
}

txBldr := authtxb.NewTxBuilder(GetTxEncoder(cdc), baseReq.AccountNumber,
txBldr := authtxb.NewTxBuilder(
GetTxEncoder(cdc), baseReq.AccountNumber,
baseReq.Sequence, gas, gasAdjustment, baseReq.Simulate,
baseReq.ChainID, baseReq.Memo, baseReq.Fees)
baseReq.ChainID, baseReq.Memo, baseReq.Fees, baseReq.GasPrices,
)

if baseReq.Simulate || simulateAndExecute {
if gasAdjustment < 0 {
Expand Down
Loading