Skip to content

Commit 68ee2eb

Browse files
committed
auth: Don't recalculate mempool fees for every msg signer, misc. cleanup
This PR begins improving the godocs for the auth module, and begins cleaning up the Ante handler. Additionally we previously calculated if the fee was sufficient for the tx on every single signer. This is now refactored to be more efficient, and have a better logical flow. No changelog entry as this is new to this release.
1 parent e2da4ca commit 68ee2eb

File tree

2 files changed

+76
-48
lines changed

2 files changed

+76
-48
lines changed

x/auth/account.go

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,12 @@ import (
88
"github.com/tendermint/tendermint/crypto"
99
)
1010

11-
// Account is a standard account using a sequence number for replay protection
12-
// and a pubkey for authentication.
11+
// Account is an interface used to store coins at a given address within state.
12+
// It presumes a notion of sequence numbers for replay protection,
13+
// a notion of account numbers for replay protection for previously pruned accounts,
14+
// and a pubkey for authentication purposes.
15+
//
16+
// Many complex conditions can be used in the concrete struct which implements Account.
1317
type Account interface {
1418
GetAddress() sdk.AccAddress
1519
SetAddress(sdk.AccAddress) error // errors if already set.
@@ -35,9 +39,11 @@ type AccountDecoder func(accountBytes []byte) (Account, error)
3539

3640
var _ Account = (*BaseAccount)(nil)
3741

38-
// BaseAccount - base account structure.
39-
// Extend this by embedding this in your AppAccount.
40-
// See the examples/basecoin/types/account.go for an example.
42+
// BaseAccount - a base account structure.
43+
// This can be extended by embedding within in your AppAccount.
44+
// There are examples of this in: examples/basecoin/types/account.go.
45+
// However one doesn't have to use BaseAccount as long as your struct
46+
// implements Account.
4147
type BaseAccount struct {
4248
Address sdk.AccAddress `json:"address"`
4349
Coins sdk.Coins `json:"coins"`
@@ -118,7 +124,7 @@ func (acc *BaseAccount) SetSequence(seq int64) error {
118124
//----------------------------------------
119125
// Wire
120126

121-
// Most users shouldn't use this, but this comes handy for tests.
127+
// Most users shouldn't use this, but this comes in handy for tests.
122128
func RegisterBaseAccount(cdc *codec.Codec) {
123129
cdc.RegisterInterface((*Account)(nil), nil)
124130
cdc.RegisterConcrete(&BaseAccount{}, "cosmos-sdk/BaseAccount", nil)

x/auth/ante.go

Lines changed: 64 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,18 @@ import (
1212
)
1313

1414
const (
15-
deductFeesCost sdk.Gas = 10
16-
memoCostPerByte sdk.Gas = 1
17-
ed25519VerifyCost = 59
18-
secp256k1VerifyCost = 100
19-
maxMemoCharacters = 100
20-
feeDeductionGasFactor = 0.001
15+
deductFeesCost sdk.Gas = 10
16+
memoCostPerByte sdk.Gas = 1
17+
ed25519VerifyCost = 59
18+
secp256k1VerifyCost = 100
19+
maxMemoCharacters = 100
20+
gasPrice = 0.001
2121
)
2222

2323
// NewAnteHandler returns an AnteHandler that checks
2424
// and increments sequence numbers, checks signatures & account numbers,
2525
// and deducts fees from the first signer.
26-
// nolint: gocyclo
2726
func NewAnteHandler(am AccountMapper, fck FeeCollectionKeeper) sdk.AnteHandler {
28-
2927
return func(
3028
ctx sdk.Context, tx sdk.Tx, simulate bool,
3129
) (newCtx sdk.Context, res sdk.Result, abort bool) {
@@ -36,13 +34,17 @@ func NewAnteHandler(am AccountMapper, fck FeeCollectionKeeper) sdk.AnteHandler {
3634
return ctx, sdk.ErrInternal("tx must be StdTx").Result(), true
3735
}
3836

39-
// set the gas meter
40-
if simulate {
41-
newCtx = ctx.WithGasMeter(sdk.NewInfiniteGasMeter())
42-
} else {
43-
newCtx = ctx.WithGasMeter(sdk.NewGasMeter(stdTx.Fee.Gas))
37+
// Ensure that the provided fees meet a minimum threshold for the validator, if this is a CheckTx.
38+
// This is for mempool purposes, and thus is only ran on check tx.
39+
if ctx.IsCheckTx() && !simulate {
40+
res := ensureSufficientMempoolFees(ctx, stdTx)
41+
if !res.IsOK() {
42+
return newCtx, res, true
43+
}
4444
}
4545

46+
newCtx = setGasMeter(simulate, ctx, stdTx)
47+
4648
// AnteHandlers must have their own defer/recover in order
4749
// for the BaseApp to know how much gas was used!
4850
// This is because the GasMeter is created in the AnteHandler,
@@ -66,52 +68,37 @@ func NewAnteHandler(am AccountMapper, fck FeeCollectionKeeper) sdk.AnteHandler {
6668
if err != nil {
6769
return newCtx, err.Result(), true
6870
}
71+
// charge gas for the memo
72+
newCtx.GasMeter().ConsumeGas(memoCostPerByte*sdk.Gas(len(stdTx.GetMemo())), "memo")
6973

70-
sigs := stdTx.GetSignatures() // When simulating, this would just be a 0-length slice.
74+
// stdSigs contains the sequence number, account number, and signatures
75+
stdSigs := stdTx.GetSignatures() // When simulating, this would just be a 0-length slice.
7176
signerAddrs := stdTx.GetSigners()
72-
msgs := tx.GetMsgs()
7377

74-
// charge gas for the memo
75-
newCtx.GasMeter().ConsumeGas(memoCostPerByte*sdk.Gas(len(stdTx.GetMemo())), "memo")
78+
// create the list of all sign bytes
79+
signBytesList := getSignBytesList(newCtx.ChainID(), stdTx, stdSigs)
7680

7781
// Get the sign bytes (requires all account & sequence numbers and the fee)
78-
sequences := make([]int64, len(sigs))
79-
accNums := make([]int64, len(sigs))
80-
for i := 0; i < len(sigs); i++ {
81-
sequences[i] = sigs[i].Sequence
82-
accNums[i] = sigs[i].AccountNumber
83-
}
84-
fee := stdTx.Fee
85-
8682
// Check sig and nonce and collect signer accounts.
8783
var signerAccs = make([]Account, len(signerAddrs))
88-
for i := 0; i < len(sigs); i++ {
89-
signerAddr, sig := signerAddrs[i], sigs[i]
84+
for i := 0; i < len(stdSigs); i++ {
85+
signerAddr, sig := signerAddrs[i], stdSigs[i]
9086

9187
// check signature, return account with incremented nonce
92-
signBytes := StdSignBytes(newCtx.ChainID(), accNums[i], sequences[i], fee, msgs, stdTx.GetMemo())
93-
signerAcc, res := processSig(newCtx, am, signerAddr, sig, signBytes, simulate)
88+
signerAcc, res := processSig(newCtx, am, signerAddr, sig, signBytesList[i], simulate)
9489
if !res.IsOK() {
9590
return newCtx, res, true
9691
}
9792

98-
requiredFees := adjustFeesByGas(ctx.MinimumFees(), fee.Gas)
99-
// fees must be greater than the minimum set by the validator adjusted by gas
100-
if ctx.IsCheckTx() && !simulate && !ctx.MinimumFees().IsZero() && fee.Amount.IsLT(requiredFees) {
101-
// validators reject any tx from the mempool with less than the minimum fee per gas * gas factor
102-
return newCtx, sdk.ErrInsufficientFee(fmt.Sprintf(
103-
"insufficient fee, got: %q required: %q", fee.Amount, requiredFees)).Result(), true
104-
}
105-
10693
// first sig pays the fees
107-
// Can this function be moved outside of the loop?
108-
if i == 0 && !fee.Amount.IsZero() {
94+
// TODO: move outside of the loop
95+
if i == 0 && !stdTx.Fee.Amount.IsZero() {
10996
newCtx.GasMeter().ConsumeGas(deductFeesCost, "deductFees")
110-
signerAcc, res = deductFees(signerAcc, fee)
97+
signerAcc, res = deductFees(signerAcc, stdTx.Fee)
11198
if !res.IsOK() {
11299
return newCtx, res, true
113100
}
114-
fck.addCollectedFees(newCtx, fee.Amount)
101+
fck.addCollectedFees(newCtx, stdTx.Fee.Amount)
115102
}
116103

117104
// Save the account.
@@ -153,6 +140,7 @@ func validateBasic(tx StdTx) (err sdk.Error) {
153140

154141
// verify the signature and increment the sequence.
155142
// if the account doesn't have a pubkey, set it.
143+
// TODO: Change this function to already take in the account
156144
func processSig(
157145
ctx sdk.Context, am AccountMapper,
158146
addr sdk.AccAddress, sig StdSignature, signBytes []byte, simulate bool) (
@@ -245,8 +233,9 @@ func consumeSignatureVerificationGas(meter sdk.GasMeter, pubkey crypto.PubKey) {
245233
}
246234

247235
func adjustFeesByGas(fees sdk.Coins, gas int64) sdk.Coins {
248-
gasCost := int64(float64(gas) * feeDeductionGasFactor)
236+
gasCost := int64(float64(gas) * gasPrice)
249237
gasFees := make(sdk.Coins, len(fees))
238+
// TODO: Make this not price all coins in the same way
250239
for i := 0; i < len(fees); i++ {
251240
gasFees[i] = sdk.NewInt64Coin(fees[i].Denom, gasCost)
252241
}
@@ -273,5 +262,38 @@ func deductFees(acc Account, fee StdFee) (Account, sdk.Result) {
273262
return acc, sdk.Result{}
274263
}
275264

265+
func ensureSufficientMempoolFees(ctx sdk.Context, stdTx StdTx) sdk.Result {
266+
// currently we use a very primitive gas pricing model with a constant gasPrice.
267+
// adjustFeesByGas handles calculating the amount of fees required based on the provided gas.
268+
// TODO: Make the gasPrice not a constant, and account for tx size.
269+
requiredFees := adjustFeesByGas(ctx.MinimumFees(), stdTx.Fee.Gas)
270+
271+
if !ctx.MinimumFees().IsZero() && stdTx.Fee.Amount.IsLT(requiredFees) {
272+
// validators reject any tx from the mempool with less than the minimum fee per gas * gas factor
273+
return sdk.ErrInsufficientFee(fmt.Sprintf(
274+
"insufficient fee, got: %q required: %q", stdTx.Fee.Amount, requiredFees)).Result()
275+
}
276+
return sdk.Result{}
277+
}
278+
279+
func setGasMeter(simulate bool, ctx sdk.Context, stdTx StdTx) sdk.Context {
280+
// set the gas meter
281+
if simulate {
282+
return ctx.WithGasMeter(sdk.NewInfiniteGasMeter())
283+
}
284+
return ctx.WithGasMeter(sdk.NewGasMeter(stdTx.Fee.Gas))
285+
}
286+
287+
func getSignBytesList(chainID string, stdTx StdTx, stdSigs []StdSignature) (
288+
signatureBytesList [][]byte) {
289+
signatureBytesList = make([][]byte, len(stdSigs))
290+
for i := 0; i < len(stdSigs); i++ {
291+
signatureBytesList[i] = StdSignBytes(chainID,
292+
stdSigs[i].AccountNumber, stdSigs[i].Sequence,
293+
stdTx.Fee, stdTx.Msgs, stdTx.Memo)
294+
}
295+
return
296+
}
297+
276298
// BurnFeeHandler burns all fees (decreasing total supply)
277299
func BurnFeeHandler(_ sdk.Context, _ sdk.Tx, _ sdk.Coins) {}

0 commit comments

Comments
 (0)