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

fix(tm2): deduct gas fee after runTx #3209

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
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
24 changes: 12 additions & 12 deletions gno.land/cmd/gnoland/testdata/addpkg_namespace.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -14,70 +14,70 @@ gnoland start

# Check if sys/users is disabled
# gui call -> sys/users.IsEnable
gnokey maketx call -pkgpath gno.land/r/sys/users -func IsEnabled -gas-fee 100000ugnot -gas-wanted 2000000 -broadcast -chainid tendermint_test gui
gnokey maketx call -pkgpath gno.land/r/sys/users -func IsEnabled -gas-fee 5ugnot -gas-wanted 2000000 -broadcast -chainid tendermint_test gui
stdout 'OK!'
stdout 'false'

# Gui should be able to addpkg on test1 addr
# gui addpkg -> gno.land/r/<addr_test1>/mysuperpkg
gnokey maketx addpkg -pkgdir $WORK -pkgpath gno.land/r/$USER_ADDR_test1/mysuperpkg -gas-fee 1000000ugnot -gas-wanted 100000000 -broadcast -chainid=tendermint_test gui
gnokey maketx addpkg -pkgdir $WORK -pkgpath gno.land/r/$USER_ADDR_test1/mysuperpkg -gas-fee 10ugnot -gas-wanted 100000000 -broadcast -chainid=tendermint_test gui
stdout 'OK!'

# Gui should be able to addpkg on random name
# gui addpkg -> gno.land/r/randomname/mysuperpkg
gnokey maketx addpkg -pkgdir $WORK -pkgpath gno.land/r/randomname/mysuperpkg -gas-fee 1000000ugnot -gas-wanted 100000000 -broadcast -chainid=tendermint_test gui
gnokey maketx addpkg -pkgdir $WORK -pkgpath gno.land/r/randomname/mysuperpkg -gas-fee 5ugnot -gas-wanted 100000000 -broadcast -chainid=tendermint_test gui
stdout 'OK!'

## When `sys/users` is enabled

# Enable `sys/users`
# admin call -> sys/users.AdminEnable
gnokey maketx call -pkgpath gno.land/r/sys/users -func AdminEnable -gas-fee 100000ugnot -gas-wanted 2000000 -broadcast -chainid tendermint_test admin
gnokey maketx call -pkgpath gno.land/r/sys/users -func AdminEnable -gas-fee 10ugnot -gas-wanted 2000000 -broadcast -chainid tendermint_test admin
stdout 'OK!'

# Check that `sys/users` has been enabled
# gui call -> sys/users.IsEnable
gnokey maketx call -pkgpath gno.land/r/sys/users -func IsEnabled -gas-fee 100000ugnot -gas-wanted 2000000 -broadcast -chainid tendermint_test gui
gnokey maketx call -pkgpath gno.land/r/sys/users -func IsEnabled -gas-fee 5ugnot -gas-wanted 2000000 -broadcast -chainid tendermint_test gui
stdout 'OK!'
stdout 'true'

# Try to add a pkg an with unregistered user
# gui addpkg -> gno.land/r/<addr_test1>/one
! gnokey maketx addpkg -pkgdir $WORK -pkgpath gno.land/r/$USER_ADDR_test1/one -gas-fee 1000000ugnot -gas-wanted 100000000 -broadcast -chainid=tendermint_test gui
! gnokey maketx addpkg -pkgdir $WORK -pkgpath gno.land/r/$USER_ADDR_test1/one -gas-fee 5ugnot -gas-wanted 100000000 -broadcast -chainid=tendermint_test gui
stderr 'unauthorized user'

# Try to add a pkg with an unregistered user, on their own address as namespace
# gui addpkg -> gno.land/r/<addr_gui>/one
gnokey maketx addpkg -pkgdir $WORK -pkgpath gno.land/r/$USER_ADDR_gui/one -gas-fee 1000000ugnot -gas-wanted 100000000 -broadcast -chainid=tendermint_test gui
gnokey maketx addpkg -pkgdir $WORK -pkgpath gno.land/r/$USER_ADDR_gui/one -gas-fee 5ugnot -gas-wanted 100000000 -broadcast -chainid=tendermint_test gui
stdout 'OK!'

## Test unregistered namespace

# Call addpkg with admin user on gui namespace
# admin addpkg -> gno.land/r/guiland/one
! gnokey maketx addpkg -pkgdir $WORK -pkgpath gno.land/r/guiland/one -gas-fee 1000000ugnot -gas-wanted 100000000 -broadcast -chainid=tendermint_test admin
! gnokey maketx addpkg -pkgdir $WORK -pkgpath gno.land/r/guiland/one -gas-fee 10ugnot -gas-wanted 100000000 -broadcast -chainid=tendermint_test admin
stderr 'unauthorized user'

## Test registered namespace

# Test admin invites gui
# admin call -> demo/users.Invite
gnokey maketx call -pkgpath gno.land/r/demo/users -func Invite -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test -args $USER_ADDR_gui admin
gnokey maketx call -pkgpath gno.land/r/demo/users -func Invite -gas-fee 10ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test -args $USER_ADDR_gui admin
stdout 'OK!'

# test gui register namespace
# gui call -> demo/users.Register
gnokey maketx call -pkgpath gno.land/r/demo/users -func Register -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test -args $USER_ADDR_admin -args 'guiland' -args 'im gui' gui
gnokey maketx call -pkgpath gno.land/r/demo/users -func Register -gas-fee 2ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test -args $USER_ADDR_admin -args 'guiland' -args 'im gui' gui
stdout 'OK!'

# Test gui publishing on guiland/one
# gui addpkg -> gno.land/r/guiland/one
gnokey maketx addpkg -pkgdir $WORK -pkgpath gno.land/r/guiland/one -gas-fee 1000000ugnot -gas-wanted 100000000 -broadcast -chainid=tendermint_test gui
gnokey maketx addpkg -pkgdir $WORK -pkgpath gno.land/r/guiland/one -gas-fee 5ugnot -gas-wanted 100000000 -broadcast -chainid=tendermint_test gui
stdout 'OK!'

# Test admin publishing on guiland/two
# admin addpkg -> gno.land/r/guiland/two
! gnokey maketx addpkg -pkgdir $WORK -pkgpath gno.land/r/guiland/two -gas-fee 1000000ugnot -gas-wanted 100000000 -broadcast -chainid=tendermint_test admin
! gnokey maketx addpkg -pkgdir $WORK -pkgpath gno.land/r/guiland/two -gas-fee 10ugnot -gas-wanted 100000000 -broadcast -chainid=tendermint_test admin
stderr 'unauthorized user'

-- one.gno --
Expand Down
19 changes: 19 additions & 0 deletions gno.land/cmd/gnoland/testdata/deduct_gasfee.txtar
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# load the package
loadpkg gno.land/r/demo/foo20

# start a new node
gnoland start

# 1. check previous balance
gnokey query bank/balances/g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5
stdout 'height: 0'
stdout 'data: "10000000000000ugnot"'

# 2. execute the Faucet function
gnokey maketx call -pkgpath gno.land/r/demo/foo20 -func Faucet -broadcast=true -chainid=tendermint_test -gas-fee 1ugnot -gas-wanted 100000000 -memo "" test1
stdout 'GAS USED: 747265'

# 3. check the balance after the Faucet function
gnokey query bank/balances/g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5
stdout 'height: 0'
stdout 'data: "9999999252735ugnot"'
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ gnokey maketx addpkg -pkgdir $WORK/short -pkgpath gno.land/r/test/realm_banker -
gnokey maketx addpkg -pkgdir $WORK/long -pkgpath gno.land/r/test/package89_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_1234567890 -gas-fee 1000000ugnot -gas-wanted 100000000 -broadcast -chainid=tendermint_test test1

## test2 spend all balance
gnokey maketx send -send "9999999ugnot" -to g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5 -gas-fee 1ugnot -gas-wanted 10000000 -broadcast -chainid=tendermint_test test2
gnokey maketx send -send "9970973ugnot" -to g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5 -gas-fee 1ugnot -gas-wanted 10000000 -broadcast -chainid=tendermint_test test2

## check test2 balance
gnokey query bank/balances/${USER_ADDR_test2}
Expand All @@ -33,8 +33,8 @@ gnokey maketx call -pkgpath gno.land/r/test/realm_banker -func Burn -args ${USER
gnokey query bank/balances/${USER_ADDR_test2}
stdout '"31330/gno.land/r/test/realm_banker:ugnot"'

## transfer 1ugnot to test2 for gas-fee of below tx
gnokey maketx send -send "1ugnot" -to ${USER_ADDR_test2} -gas-fee 1ugnot -gas-wanted 10000000 -broadcast -chainid=tendermint_test test1
## transfer 33387ugnot to test2 for gas-fee of below tx
gnokey maketx send -send "33387ugnot" -to ${USER_ADDR_test2} -gas-fee 1ugnot -gas-wanted 10000000 -broadcast -chainid=tendermint_test test1

## transfer coin
gnokey maketx send -send "1330/gno.land/r/test/realm_banker:ugnot" -to g1yr0dpfgthph7y6mepdx8afuec4q3ga2lg8tjt0 -gas-fee 1ugnot -gas-wanted 10000000 -broadcast -chainid=tendermint_test test2
Expand Down
6 changes: 3 additions & 3 deletions gno.land/cmd/gnoland/testdata/restart_missing_type.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ gnoland restart
],
"fee": {
"gas_wanted": "1000000",
"gas_fee": "1000000ugnot"
"gas_fee": "10ugnot"
},
"signatures": [],
"memo": ""
Expand Down Expand Up @@ -163,7 +163,7 @@ gnoland restart
],
"fee": {
"gas_wanted": "20000000",
"gas_fee": "1000000ugnot"
"gas_fee": "10ugnot"
},
"signatures": [],
"memo": ""
Expand Down Expand Up @@ -194,7 +194,7 @@ gnoland restart
],
"fee": {
"gas_wanted": "16000000",
"gas_fee": "1000000ugnot"
"gas_fee": "10ugnot"
},
"signatures": [],
"memo": ""
Expand Down
8 changes: 8 additions & 0 deletions gno.land/pkg/gnoland/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,14 @@ func NewAppWithOptions(cfg *AppOptions) (abci.Application, error) {
return
},
)
// Set GasFeeCollector
gasFeeCollector := auth.NewGasFeeCollector(acctKpr, bankKpr)
baseApp.SetGasFeeCollector(func(ctx sdk.Context, tx std.Tx, gasUsed int64) (
res sdk.Result,
) {
res = gasFeeCollector(ctx, tx, gasUsed)
return
})

// Set begin and end transaction hooks.
// These are used to create gno transaction stores and commit them when finishing
Expand Down
2 changes: 1 addition & 1 deletion gno.land/pkg/gnoland/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ func createAndSignTx(
tx := std.Tx{
Msgs: msgs,
Fee: std.Fee{
GasFee: std.NewCoin("ugnot", 2000000),
GasFee: std.NewCoin("ugnot", 20),
GasWanted: 10000000,
},
}
Expand Down
4 changes: 2 additions & 2 deletions gno.land/pkg/integration/testdata/adduser.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ adduser test8
gnoland start

## add bar.gno package located in $WORK directory as gno.land/r/foobar/bar
gnokey maketx addpkg -pkgdir $WORK/bar -pkgpath gno.land/r/foobar/bar -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test8
gnokey maketx addpkg -pkgdir $WORK/bar -pkgpath gno.land/r/foobar/bar -gas-fee 10ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test8

## execute Render
gnokey maketx run -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test8 $WORK/script/script.gno
gnokey maketx run -gas-fee 10ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test8 $WORK/script/script.gno

## compare render
stdout 'main: --- hello from foo ---'
Expand Down
42 changes: 31 additions & 11 deletions tm2/pkg/sdk/auth/ante.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,17 +123,6 @@ func NewAnteHandler(ak AccountKeeper, bank BankKeeperI, sigGasConsumer Signature
return newCtx, res, true
}

// deduct the fees
if !tx.Fee.GasFee.IsZero() {
res = DeductFees(bank, newCtx, signerAccs[0], std.Coins{tx.Fee.GasFee})
if !res.IsOK() {
return newCtx, res, true
}

// reload the account as fees have been deducted
signerAccs[0] = ak.GetAccount(newCtx, signerAccs[0].GetAddress())
}

// stdSigs contains the sequence number, account number, and signatures.
// When simulating, this would just be a 0-length slice.
stdSigs := tx.GetSignatures()
Expand Down Expand Up @@ -344,6 +333,37 @@ func consumeMultisignatureVerificationGas(meter store.GasMeter,
}
}

// NewGasFeeCollector returns a new GasFeeCollector that will be used to
// deduct fees from the first signer.
func NewGasFeeCollector(ak AccountKeeper, bank BankKeeperI) sdk.GasFeeCollector {
return func(
ctx sdk.Context,
tx std.Tx,
gasUsed int64,
) (res sdk.Result) {
signerAddrs := tx.GetSigners()
signerAccs := make([]std.Account, len(signerAddrs))
signerAccs[0], res = GetSignerAcc(ctx, ak, signerAddrs[0])
if !res.IsOK() {
return res
}

if !tx.Fee.GasFee.IsZero() {
collectGasFee := std.NewCoin(
tx.Fee.GasFee.Denom,
tx.Fee.GasFee.Amount*gasUsed,
)
res = DeductFees(bank, ctx, signerAccs[0], std.Coins{collectGasFee})
if !res.IsOK() {
return res
}
} else {
return abciResult(std.ErrInsufficientFee(fmt.Sprintf("invalid fee amount: %s", tx.Fee.GasFee)))
}
return sdk.Result{GasWanted: tx.Fee.GasWanted}
}
}

// DeductFees deducts fees from the given account.
//
// NOTE: We could use the CoinKeeper (in addition to the AccountKeeper, because
Expand Down
134 changes: 91 additions & 43 deletions tm2/pkg/sdk/auth/ante_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,25 @@ func defaultAnteOptions() AnteOptions {
}
}

// run the tx through the gasFeeCollector and ensure its valid
func checkGasFeeValidTx(t *testing.T, gasFeeCollector sdk.GasFeeCollector, ctx sdk.Context, tx std.Tx, gasUsed int64) {
t.Helper()

result := gasFeeCollector(ctx, tx, gasUsed)
require.Nil(t, result.Error)
require.Equal(t, tx.Fee.GasWanted, result.GasWanted)
require.True(t, result.IsOK())
}

// run the tx through the anteHandler and ensure it fails with the given code
func checkGasFeeInvalidTx(t *testing.T, gasFeeCollector sdk.GasFeeCollector, ctx sdk.Context, tx std.Tx, gasUsed int64, err abci.Error) {
t.Helper()

result := gasFeeCollector(ctx, tx, gasUsed)
require.True(t, result.IsErr())
require.Equal(t, reflect.TypeOf(err), reflect.TypeOf(sdk.ABCIError(result.Error)), fmt.Sprintf("Expected %v, got %v", err, result))
}

// Test various error cases in the AnteHandler control flow.
func TestAnteHandlerSigErrors(t *testing.T) {
t.Parallel()
Expand Down Expand Up @@ -306,49 +325,6 @@ func TestAnteHandlerSequences(t *testing.T) {
checkValidTx(t, anteHandler, ctx, tx, false)
}

// Test logic around fee deduction.
func TestAnteHandlerFees(t *testing.T) {
t.Parallel()

// setup
env := setupTestEnv()
ctx := env.ctx
anteHandler := NewAnteHandler(env.acck, env.bank, DefaultSigVerificationGasConsumer, defaultAnteOptions())

// keys and addresses
priv1, _, addr1 := tu.KeyTestPubAddr()

// set the accounts
acc1 := env.acck.NewAccountWithAddress(ctx, addr1)
env.acck.SetAccount(ctx, acc1)

// msg and signatures
var tx std.Tx
msg := tu.NewTestMsg(addr1)
privs, accnums, seqs := []crypto.PrivKey{priv1}, []uint64{0}, []uint64{0}
fee := tu.NewTestFee()
msgs := []std.Msg{msg}

// signer does not have enough funds to pay the fee
tx = tu.NewTestTx(t, ctx.ChainID(), msgs, privs, accnums, seqs, fee)
checkInvalidTx(t, anteHandler, ctx, tx, false, std.InsufficientFundsError{})

acc1.SetCoins(std.NewCoins(std.NewCoin("atom", 149)))
env.acck.SetAccount(ctx, acc1)
checkInvalidTx(t, anteHandler, ctx, tx, false, std.InsufficientFundsError{})

collector := env.bank.(DummyBankKeeper).acck.GetAccount(ctx, FeeCollectorAddress())
require.Nil(t, collector)
require.Equal(t, env.acck.GetAccount(ctx, addr1).GetCoins().AmountOf("atom"), int64(149))

acc1.SetCoins(std.NewCoins(std.NewCoin("atom", 150)))
env.acck.SetAccount(ctx, acc1)
checkValidTx(t, anteHandler, ctx, tx, false)

require.Equal(t, env.bank.(DummyBankKeeper).acck.GetAccount(ctx, FeeCollectorAddress()).GetCoins().AmountOf("atom"), int64(150))
require.Equal(t, env.acck.GetAccount(ctx, addr1).GetCoins().AmountOf("atom"), int64(0))
}

// Test logic around memo gas consumption.
func TestAnteHandlerMemoGas(t *testing.T) {
t.Parallel()
Expand Down Expand Up @@ -866,3 +842,75 @@ func TestCustomSignatureVerificationGasConsumer(t *testing.T) {
tx = tu.NewTestTx(t, ctx.ChainID(), msgs, privs, accnums, seqs, fee)
checkValidTx(t, anteHandler, ctx, tx, false)
}

// TestNewGasFeeCollector
func TestNewGasFeeCollector(t *testing.T) {
t.Parallel()

// setup
env := setupTestEnv()
ctx := env.ctx
gasFeeCollector := NewGasFeeCollector(env.acck, env.bank)

// keys and addresses
priv1, _, addr1 := tu.KeyTestPubAddr()

// msg and signatures
var tx std.Tx
msg := tu.NewTestMsg(addr1)
privs, accnums, seqs := []crypto.PrivKey{priv1}, []uint64{0}, []uint64{0}
fee := tu.NewTestFee()
msgs := []std.Msg{msg}

// test signer use new account (UnknownAddress)
tx = tu.NewTestTx(t, ctx.ChainID(), msgs, privs, accnums, seqs, fee)
checkGasFeeInvalidTx(t, gasFeeCollector, ctx, tx, int64(1000), std.UnknownAddressError{})

// set the accounts
acc1 := env.acck.NewAccountWithAddress(ctx, addr1)
env.acck.SetAccount(ctx, acc1)

// test signer does not set gas-fee
zeroGasFee := std.NewFee(50000, std.NewCoin("atom", 0))
tx = tu.NewTestTx(t, ctx.ChainID(), msgs, privs, accnums, seqs, zeroGasFee)
checkGasFeeInvalidTx(t, gasFeeCollector, ctx, tx, int64(1000), std.InsufficientFeeError{})

// test signer does not have enough funds to pay the fee
tx = tu.NewTestTx(t, ctx.ChainID(), msgs, privs, accnums, seqs, fee)
checkGasFeeInvalidTx(t, gasFeeCollector, ctx, tx, int64(1000), std.InsufficientFundsError{})

// test inValid fee
t.Run("invalid denom test", func(t *testing.T) {
t.Parallel()

invalidDenomGasFee := std.NewFee(50000, std.Coin{Denom: "atom@", Amount: 100})
invalidDenomGasFee2 := std.NewFee(50000, std.Coin{Denom: "atom denom", Amount: 100})
invalidDenomGasFee3 := std.NewFee(50000, std.Coin{Denom: "a", Amount: 100})

txInvalidDenom := tu.NewTestTx(t, ctx.ChainID(), msgs, privs, accnums, seqs, invalidDenomGasFee)
txInvalidDenom2 := tu.NewTestTx(t, ctx.ChainID(), msgs, privs, accnums, seqs, invalidDenomGasFee2)
txInvalidDenom3 := tu.NewTestTx(t, ctx.ChainID(), msgs, privs, accnums, seqs, invalidDenomGasFee3)

require.Panics(t, func() { gasFeeCollector(ctx, txInvalidDenom, int64(1000)) })
require.Panics(t, func() { gasFeeCollector(ctx, txInvalidDenom2, int64(1000)) })
require.Panics(t, func() { gasFeeCollector(ctx, txInvalidDenom3, int64(1000)) })
})

// test signer does not have enough funds to pay the fee
notEnoughFee := std.NewFee(50000, std.NewCoin("atom", 100))
tx = tu.NewTestTx(t, ctx.ChainID(), msgs, privs, accnums, seqs, notEnoughFee)
checkGasFeeInvalidTx(t, gasFeeCollector, ctx, tx, int64(1000), std.InsufficientFundsError{})

// test good tx from one signer
acc1.SetCoins(tu.NewTestCoins())
env.acck.SetAccount(ctx, acc1)
require.Equal(t, env.acck.GetAccount(ctx, addr1).GetCoins().AmountOf("atom"), int64(10000000))

collector := env.bank.(DummyBankKeeper).acck.GetAccount(ctx, FeeCollectorAddress())
require.Nil(t, collector)
tx = tu.NewTestTx(t, ctx.ChainID(), msgs, privs, accnums, seqs, fee)
checkGasFeeValidTx(t, gasFeeCollector, ctx, tx, int64(1000))

require.Equal(t, env.bank.(DummyBankKeeper).acck.GetAccount(ctx, FeeCollectorAddress()).GetCoins().AmountOf("atom"), int64(150000))
require.Equal(t, env.acck.GetAccount(ctx, addr1).GetCoins().AmountOf("atom"), int64(9850000))
}
Loading
Loading