Merge pull request #1064 from cosmos/sunny/ante_fee_handle

Fee Collection in auth
This commit is contained in:
Rigel 2018-05-26 04:28:44 -04:00 committed by GitHub
commit cb1b6baa19
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 186 additions and 32 deletions

View File

@ -40,10 +40,11 @@ type GaiaApp struct {
keyStake *sdk.KVStoreKey keyStake *sdk.KVStoreKey
// Manage getting and setting accounts // Manage getting and setting accounts
accountMapper auth.AccountMapper accountMapper auth.AccountMapper
coinKeeper bank.Keeper feeCollectionKeeper auth.FeeCollectionKeeper
ibcMapper ibc.Mapper coinKeeper bank.Keeper
stakeKeeper stake.Keeper ibcMapper ibc.Mapper
stakeKeeper stake.Keeper
} }
func NewGaiaApp(logger log.Logger, db dbm.DB) *GaiaApp { func NewGaiaApp(logger log.Logger, db dbm.DB) *GaiaApp {
@ -81,7 +82,7 @@ func NewGaiaApp(logger log.Logger, db dbm.DB) *GaiaApp {
app.SetInitChainer(app.initChainer) app.SetInitChainer(app.initChainer)
app.SetEndBlocker(stake.NewEndBlocker(app.stakeKeeper)) app.SetEndBlocker(stake.NewEndBlocker(app.stakeKeeper))
app.MountStoresIAVL(app.keyMain, app.keyAccount, app.keyIBC, app.keyStake) app.MountStoresIAVL(app.keyMain, app.keyAccount, app.keyIBC, app.keyStake)
app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper)) app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper, app.feeCollectionKeeper))
err := app.LoadLatestVersion(app.keyMain) err := app.LoadLatestVersion(app.keyMain)
if err != nil { if err != nil {
cmn.Exit(err.Error()) cmn.Exit(err.Error())

View File

@ -35,10 +35,11 @@ type BasecoinApp struct {
keyStake *sdk.KVStoreKey keyStake *sdk.KVStoreKey
// Manage getting and setting accounts // Manage getting and setting accounts
accountMapper auth.AccountMapper accountMapper auth.AccountMapper
coinKeeper bank.Keeper feeCollectionKeeper auth.FeeCollectionKeeper
ibcMapper ibc.Mapper coinKeeper bank.Keeper
stakeKeeper stake.Keeper ibcMapper ibc.Mapper
stakeKeeper stake.Keeper
} }
func NewBasecoinApp(logger log.Logger, db dbm.DB) *BasecoinApp { func NewBasecoinApp(logger log.Logger, db dbm.DB) *BasecoinApp {
@ -78,7 +79,7 @@ func NewBasecoinApp(logger log.Logger, db dbm.DB) *BasecoinApp {
// Initialize BaseApp. // Initialize BaseApp.
app.SetInitChainer(app.initChainer) app.SetInitChainer(app.initChainer)
app.MountStoresIAVL(app.keyMain, app.keyAccount, app.keyIBC, app.keyStake) app.MountStoresIAVL(app.keyMain, app.keyAccount, app.keyIBC, app.keyStake)
app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper)) app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper, app.feeCollectionKeeper))
err := app.LoadLatestVersion(app.keyMain) err := app.LoadLatestVersion(app.keyMain)
if err != nil { if err != nil {
cmn.Exit(err.Error()) cmn.Exit(err.Error())

View File

@ -39,11 +39,12 @@ type DemocoinApp struct {
capKeyStakingStore *sdk.KVStoreKey capKeyStakingStore *sdk.KVStoreKey
// keepers // keepers
coinKeeper bank.Keeper feeCollectionKeeper auth.FeeCollectionKeeper
coolKeeper cool.Keeper coinKeeper bank.Keeper
powKeeper pow.Keeper coolKeeper cool.Keeper
ibcMapper ibc.Mapper powKeeper pow.Keeper
stakeKeeper simplestake.Keeper ibcMapper ibc.Mapper
stakeKeeper simplestake.Keeper
// Manage getting and setting accounts // Manage getting and setting accounts
accountMapper auth.AccountMapper accountMapper auth.AccountMapper
@ -89,7 +90,7 @@ func NewDemocoinApp(logger log.Logger, db dbm.DB) *DemocoinApp {
// Initialize BaseApp. // Initialize BaseApp.
app.SetInitChainer(app.initChainerFn(app.coolKeeper, app.powKeeper)) app.SetInitChainer(app.initChainerFn(app.coolKeeper, app.powKeeper))
app.MountStoresIAVL(app.capKeyMainStore, app.capKeyAccountStore, app.capKeyPowStore, app.capKeyIBCStore, app.capKeyStakingStore) app.MountStoresIAVL(app.capKeyMainStore, app.capKeyAccountStore, app.capKeyPowStore, app.capKeyIBCStore, app.capKeyStakingStore)
app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper)) app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper, app.feeCollectionKeeper))
err := app.LoadLatestVersion(app.capKeyMainStore) err := app.LoadLatestVersion(app.capKeyMainStore)
if err != nil { if err != nil {
cmn.Exit(err.Error()) cmn.Exit(err.Error())

View File

@ -9,13 +9,14 @@ import (
) )
const ( const (
verifyCost = 100 deductFeesCost sdk.Gas = 10
verifyCost = 100
) )
// NewAnteHandler returns an AnteHandler that checks // NewAnteHandler returns an AnteHandler that checks
// and increments sequence numbers, checks signatures, // and increments sequence numbers, checks signatures,
// and deducts fees from the first signer. // and deducts fees from the first signer.
func NewAnteHandler(am AccountMapper) sdk.AnteHandler { func NewAnteHandler(am AccountMapper, fck FeeCollectionKeeper) sdk.AnteHandler {
return func( return func(
ctx sdk.Context, tx sdk.Tx, ctx sdk.Context, tx sdk.Tx,
@ -77,10 +78,12 @@ func NewAnteHandler(am AccountMapper) sdk.AnteHandler {
if i == 0 { if i == 0 {
// TODO: min fee // TODO: min fee
if !fee.Amount.IsZero() { if !fee.Amount.IsZero() {
ctx.GasMeter().ConsumeGas(deductFeesCost, "deductFees")
signerAcc, res = deductFees(signerAcc, fee) signerAcc, res = deductFees(signerAcc, fee)
if !res.IsOK() { if !res.IsOK() {
return ctx, res, true return ctx, res, true
} }
fck.addCollectedFees(ctx, fee.Amount)
} }
} }

View File

@ -69,11 +69,12 @@ func newTestTxWithSignBytes(msg sdk.Msg, privs []crypto.PrivKey, seqs []int64, f
// Test various error cases in the AnteHandler control flow. // Test various error cases in the AnteHandler control flow.
func TestAnteHandlerSigErrors(t *testing.T) { func TestAnteHandlerSigErrors(t *testing.T) {
// setup // setup
ms, capKey := setupMultiStore() ms, capKey, capKey2 := setupMultiStore()
cdc := wire.NewCodec() cdc := wire.NewCodec()
RegisterBaseAccount(cdc) RegisterBaseAccount(cdc)
mapper := NewAccountMapper(cdc, capKey, &BaseAccount{}) mapper := NewAccountMapper(cdc, capKey, &BaseAccount{})
anteHandler := NewAnteHandler(mapper) feeCollector := NewFeeCollectionKeeper(cdc, capKey2)
anteHandler := NewAnteHandler(mapper, feeCollector)
ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger()) ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger())
// keys and addresses // keys and addresses
@ -110,11 +111,12 @@ func TestAnteHandlerSigErrors(t *testing.T) {
// Test logic around sequence checking with one signer and many signers. // Test logic around sequence checking with one signer and many signers.
func TestAnteHandlerSequences(t *testing.T) { func TestAnteHandlerSequences(t *testing.T) {
// setup // setup
ms, capKey := setupMultiStore() ms, capKey, capKey2 := setupMultiStore()
cdc := wire.NewCodec() cdc := wire.NewCodec()
RegisterBaseAccount(cdc) RegisterBaseAccount(cdc)
mapper := NewAccountMapper(cdc, capKey, &BaseAccount{}) mapper := NewAccountMapper(cdc, capKey, &BaseAccount{})
anteHandler := NewAnteHandler(mapper) feeCollector := NewFeeCollectionKeeper(cdc, capKey2)
anteHandler := NewAnteHandler(mapper, feeCollector)
ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger()) ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger())
// keys and addresses // keys and addresses
@ -176,11 +178,12 @@ func TestAnteHandlerSequences(t *testing.T) {
// Test logic around fee deduction. // Test logic around fee deduction.
func TestAnteHandlerFees(t *testing.T) { func TestAnteHandlerFees(t *testing.T) {
// setup // setup
ms, capKey := setupMultiStore() ms, capKey, capKey2 := setupMultiStore()
cdc := wire.NewCodec() cdc := wire.NewCodec()
RegisterBaseAccount(cdc) RegisterBaseAccount(cdc)
mapper := NewAccountMapper(cdc, capKey, &BaseAccount{}) mapper := NewAccountMapper(cdc, capKey, &BaseAccount{})
anteHandler := NewAnteHandler(mapper) feeCollector := NewFeeCollectionKeeper(cdc, capKey2)
anteHandler := NewAnteHandler(mapper, feeCollector)
ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger()) ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger())
// keys and addresses // keys and addresses
@ -206,18 +209,23 @@ func TestAnteHandlerFees(t *testing.T) {
mapper.SetAccount(ctx, acc1) mapper.SetAccount(ctx, acc1)
checkInvalidTx(t, anteHandler, ctx, tx, sdk.CodeInsufficientFunds) checkInvalidTx(t, anteHandler, ctx, tx, sdk.CodeInsufficientFunds)
assert.True(t, feeCollector.GetCollectedFees(ctx).IsEqual(emptyCoins))
acc1.SetCoins(sdk.Coins{{"atom", 150}}) acc1.SetCoins(sdk.Coins{{"atom", 150}})
mapper.SetAccount(ctx, acc1) mapper.SetAccount(ctx, acc1)
checkValidTx(t, anteHandler, ctx, tx) checkValidTx(t, anteHandler, ctx, tx)
assert.True(t, feeCollector.GetCollectedFees(ctx).IsEqual(sdk.Coins{{"atom", 150}}))
} }
func TestAnteHandlerBadSignBytes(t *testing.T) { func TestAnteHandlerBadSignBytes(t *testing.T) {
// setup // setup
ms, capKey := setupMultiStore() ms, capKey, capKey2 := setupMultiStore()
cdc := wire.NewCodec() cdc := wire.NewCodec()
RegisterBaseAccount(cdc) RegisterBaseAccount(cdc)
mapper := NewAccountMapper(cdc, capKey, &BaseAccount{}) mapper := NewAccountMapper(cdc, capKey, &BaseAccount{})
anteHandler := NewAnteHandler(mapper) feeCollector := NewFeeCollectionKeeper(cdc, capKey2)
anteHandler := NewAnteHandler(mapper, feeCollector)
ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger()) ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger())
// keys and addresses // keys and addresses
@ -288,11 +296,12 @@ func TestAnteHandlerBadSignBytes(t *testing.T) {
func TestAnteHandlerSetPubKey(t *testing.T) { func TestAnteHandlerSetPubKey(t *testing.T) {
// setup // setup
ms, capKey := setupMultiStore() ms, capKey, capKey2 := setupMultiStore()
cdc := wire.NewCodec() cdc := wire.NewCodec()
RegisterBaseAccount(cdc) RegisterBaseAccount(cdc)
mapper := NewAccountMapper(cdc, capKey, &BaseAccount{}) mapper := NewAccountMapper(cdc, capKey, &BaseAccount{})
anteHandler := NewAnteHandler(mapper) feeCollector := NewFeeCollectionKeeper(cdc, capKey2)
anteHandler := NewAnteHandler(mapper, feeCollector)
ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger()) ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger())
// keys and addresses // keys and addresses

View File

@ -12,7 +12,7 @@ import (
) )
func TestContextWithSigners(t *testing.T) { func TestContextWithSigners(t *testing.T) {
ms, _ := setupMultiStore() ms, _, _ := setupMultiStore()
ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger()) ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger())
_, _, addr1 := keyPubAddr() _, _, addr1 := keyPubAddr()

62
x/auth/feekeeper.go Normal file
View File

@ -0,0 +1,62 @@
package auth
import (
sdk "github.com/cosmos/cosmos-sdk/types"
wire "github.com/cosmos/cosmos-sdk/wire"
)
var (
collectedFeesKey = []byte("collectedFees")
)
// This FeeCollectionKeeper handles collection of fees in the anteHandler
// and setting of MinFees for different fee tokens
type FeeCollectionKeeper struct {
// The (unexposed) key used to access the fee store from the Context.
key sdk.StoreKey
// The wire codec for binary encoding/decoding of accounts.
cdc *wire.Codec
}
// NewFeeKeeper returns a new FeeKeeper
func NewFeeCollectionKeeper(cdc *wire.Codec, key sdk.StoreKey) FeeCollectionKeeper {
return FeeCollectionKeeper{
key: key,
cdc: cdc,
}
}
// Adds to Collected Fee Pool
func (fck FeeCollectionKeeper) GetCollectedFees(ctx sdk.Context) sdk.Coins {
store := ctx.KVStore(fck.key)
bz := store.Get(collectedFeesKey)
if bz == nil {
return sdk.Coins{}
}
feePool := &(sdk.Coins{})
fck.cdc.MustUnmarshalBinary(bz, feePool)
return *feePool
}
// Sets to Collected Fee Pool
func (fck FeeCollectionKeeper) setCollectedFees(ctx sdk.Context, coins sdk.Coins) {
bz := fck.cdc.MustMarshalBinary(coins)
store := ctx.KVStore(fck.key)
store.Set(collectedFeesKey, bz)
}
// Adds to Collected Fee Pool
func (fck FeeCollectionKeeper) addCollectedFees(ctx sdk.Context, coins sdk.Coins) sdk.Coins {
newCoins := fck.GetCollectedFees(ctx).Plus(coins)
fck.setCollectedFees(ctx, newCoins)
return newCoins
}
// Clears the collected Fee Pool
func (fck FeeCollectionKeeper) ClearCollectedFees(ctx sdk.Context) {
fck.setCollectedFees(ctx, sdk.Coins{})
}

75
x/auth/feekeeper_test.go Normal file
View File

@ -0,0 +1,75 @@
package auth
import (
"testing"
"github.com/stretchr/testify/assert"
abci "github.com/tendermint/abci/types"
"github.com/tendermint/tmlibs/log"
sdk "github.com/cosmos/cosmos-sdk/types"
wire "github.com/cosmos/cosmos-sdk/wire"
)
var (
emptyCoins = sdk.Coins{}
oneCoin = sdk.Coins{{"foocoin", 1}}
twoCoins = sdk.Coins{{"foocoin", 2}}
)
func TestFeeCollectionKeeperGetSet(t *testing.T) {
ms, _, capKey2 := setupMultiStore()
cdc := wire.NewCodec()
// make context and keeper
ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger())
fck := NewFeeCollectionKeeper(cdc, capKey2)
// no coins initially
currFees := fck.GetCollectedFees(ctx)
assert.True(t, currFees.IsEqual(emptyCoins))
// set feeCollection to oneCoin
fck.setCollectedFees(ctx, oneCoin)
// check that it is equal to oneCoin
assert.True(t, fck.GetCollectedFees(ctx).IsEqual(oneCoin))
}
func TestFeeCollectionKeeperAdd(t *testing.T) {
ms, _, capKey2 := setupMultiStore()
cdc := wire.NewCodec()
// make context and keeper
ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger())
fck := NewFeeCollectionKeeper(cdc, capKey2)
// no coins initially
assert.True(t, fck.GetCollectedFees(ctx).IsEqual(emptyCoins))
// add oneCoin and check that pool is now oneCoin
fck.addCollectedFees(ctx, oneCoin)
assert.True(t, fck.GetCollectedFees(ctx).IsEqual(oneCoin))
// add oneCoin again and check that pool is now twoCoins
fck.addCollectedFees(ctx, oneCoin)
assert.True(t, fck.GetCollectedFees(ctx).IsEqual(twoCoins))
}
func TestFeeCollectionKeeperClear(t *testing.T) {
ms, _, capKey2 := setupMultiStore()
cdc := wire.NewCodec()
// make context and keeper
ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger())
fck := NewFeeCollectionKeeper(cdc, capKey2)
// set coins initially
fck.setCollectedFees(ctx, twoCoins)
assert.True(t, fck.GetCollectedFees(ctx).IsEqual(twoCoins))
// clear fees and see that pool is now empty
fck.ClearCollectedFees(ctx)
assert.True(t, fck.GetCollectedFees(ctx).IsEqual(emptyCoins))
}

View File

@ -14,17 +14,19 @@ import (
wire "github.com/cosmos/cosmos-sdk/wire" wire "github.com/cosmos/cosmos-sdk/wire"
) )
func setupMultiStore() (sdk.MultiStore, *sdk.KVStoreKey) { func setupMultiStore() (sdk.MultiStore, *sdk.KVStoreKey, *sdk.KVStoreKey) {
db := dbm.NewMemDB() db := dbm.NewMemDB()
capKey := sdk.NewKVStoreKey("capkey") capKey := sdk.NewKVStoreKey("capkey")
capKey2 := sdk.NewKVStoreKey("capkey2")
ms := store.NewCommitMultiStore(db) ms := store.NewCommitMultiStore(db)
ms.MountStoreWithDB(capKey, sdk.StoreTypeIAVL, db) ms.MountStoreWithDB(capKey, sdk.StoreTypeIAVL, db)
ms.MountStoreWithDB(capKey2, sdk.StoreTypeIAVL, db)
ms.LoadLatestVersion() ms.LoadLatestVersion()
return ms, capKey return ms, capKey, capKey2
} }
func TestAccountMapperGetSet(t *testing.T) { func TestAccountMapperGetSet(t *testing.T) {
ms, capKey := setupMultiStore() ms, capKey, _ := setupMultiStore()
cdc := wire.NewCodec() cdc := wire.NewCodec()
RegisterBaseAccount(cdc) RegisterBaseAccount(cdc)