cosmos-sdk/x/auth/ante.go

253 lines
7.4 KiB
Go
Raw Normal View History

package auth
import (
2018-03-17 11:54:18 -07:00
"bytes"
2018-03-12 17:40:04 -07:00
"fmt"
2018-01-12 14:30:02 -08:00
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/ed25519"
"github.com/tendermint/tendermint/crypto/secp256k1"
)
2018-05-15 06:02:54 -07:00
const (
deductFeesCost sdk.Gas = 10
memoCostPerByte sdk.Gas = 1
ed25519VerifyCost = 59
secp256k1VerifyCost = 100
maxMemoCharacters = 100
2018-05-15 06:02:54 -07:00
)
2018-03-14 10:16:52 -07:00
// NewAnteHandler returns an AnteHandler that checks
// and increments sequence numbers, checks signatures & account numbers,
2018-03-14 10:16:52 -07:00
// and deducts fees from the first signer.
2018-07-13 11:12:29 -07:00
// nolint: gocyclo
2018-05-25 20:29:40 -07:00
func NewAnteHandler(am AccountMapper, fck FeeCollectionKeeper) sdk.AnteHandler {
2018-05-23 19:26:54 -07:00
return func(
ctx sdk.Context, tx sdk.Tx, simulate bool,
2018-07-13 10:53:12 -07:00
) (newCtx sdk.Context, res sdk.Result, abort bool) {
2018-05-23 19:26:54 -07:00
// This AnteHandler requires Txs to be StdTxs
stdTx, ok := tx.(StdTx)
if !ok {
2018-05-23 22:09:01 -07:00
return ctx, sdk.ErrInternal("tx must be StdTx").Result(), true
2018-05-23 19:26:54 -07:00
}
2018-07-13 10:53:12 -07:00
// set the gas meter
if simulate {
newCtx = ctx.WithGasMeter(sdk.NewInfiniteGasMeter())
} else {
newCtx = ctx.WithGasMeter(sdk.NewGasMeter(stdTx.Fee.Gas))
}
2018-07-13 10:53:12 -07:00
2018-07-12 19:38:22 -07:00
// AnteHandlers must have their own defer/recover in order
// for the BaseApp to know how much gas was used!
// This is because the GasMeter is created in the AnteHandler,
// but if it panics the context won't be set properly in runTx's recover ...
defer func() {
if r := recover(); r != nil {
switch rType := r.(type) {
case sdk.ErrorOutOfGas:
log := fmt.Sprintf("out of gas in location: %v", rType.Descriptor)
res = sdk.ErrOutOfGas(log).Result()
2018-07-13 10:53:12 -07:00
res.GasWanted = stdTx.Fee.Gas
res.GasUsed = newCtx.GasMeter().GasConsumed()
abort = true
2018-07-12 19:38:22 -07:00
default:
panic(r)
}
}
}()
err := validateBasic(stdTx)
if err != nil {
2018-07-13 10:53:12 -07:00
return newCtx, err.Result(), true
}
2018-01-26 04:19:33 -08:00
sigs := stdTx.GetSignatures() // When simulating, this would just be a 0-length slice.
signerAddrs := stdTx.GetSigners()
msgs := tx.GetMsgs()
// charge gas for the memo
2018-07-13 10:53:12 -07:00
newCtx.GasMeter().ConsumeGas(memoCostPerByte*sdk.Gas(len(stdTx.GetMemo())), "memo")
// Get the sign bytes (requires all account & sequence numbers and the fee)
sequences := make([]int64, len(sigs))
accNums := make([]int64, len(sigs))
for i := 0; i < len(sigs); i++ {
sequences[i] = sigs[i].Sequence
accNums[i] = sigs[i].AccountNumber
}
2018-03-17 11:54:18 -07:00
fee := stdTx.Fee
2018-03-04 00:15:26 -08:00
2018-03-14 10:16:52 -07:00
// Check sig and nonce and collect signer accounts.
2018-05-23 19:26:54 -07:00
var signerAccs = make([]Account, len(signerAddrs))
2018-03-14 10:16:52 -07:00
for i := 0; i < len(sigs); i++ {
2018-03-04 00:15:26 -08:00
signerAddr, sig := signerAddrs[i], sigs[i]
2018-03-17 11:54:18 -07:00
// check signature, return account with incremented nonce
2018-07-13 10:53:12 -07:00
signBytes := StdSignBytes(newCtx.ChainID(), accNums[i], sequences[i], fee, msgs, stdTx.GetMemo())
signerAcc, res := processSig(newCtx, am, signerAddr, sig, signBytes, simulate)
2018-03-03 23:32:39 -08:00
if !res.IsOK() {
2018-07-13 10:53:12 -07:00
return newCtx, res, true
}
2018-03-17 11:54:18 -07:00
// first sig pays the fees
// TODO: Add min fees
// Can this function be moved outside of the loop?
if i == 0 && !fee.Amount.IsZero() {
2018-07-13 10:53:12 -07:00
newCtx.GasMeter().ConsumeGas(deductFeesCost, "deductFees")
signerAcc, res = deductFees(signerAcc, fee)
if !res.IsOK() {
2018-07-13 10:53:12 -07:00
return newCtx, res, true
2018-03-17 11:54:18 -07:00
}
2018-07-13 10:53:12 -07:00
fck.addCollectedFees(newCtx, fee.Amount)
2018-03-17 11:54:18 -07:00
}
// Save the account.
2018-07-13 10:53:12 -07:00
am.SetAccount(newCtx, signerAcc)
2018-03-03 23:32:39 -08:00
signerAccs[i] = signerAcc
}
2018-03-17 11:54:18 -07:00
// cache the signer accounts in the context
2018-07-13 10:53:12 -07:00
newCtx = WithSigners(newCtx, signerAccs)
2018-03-17 11:54:18 -07:00
2018-03-14 10:16:52 -07:00
// TODO: tx tags (?)
2018-03-17 11:54:18 -07:00
2018-07-13 10:53:12 -07:00
return newCtx, sdk.Result{GasWanted: stdTx.Fee.Gas}, false // continue...
}
}
2018-03-03 23:32:39 -08:00
// Validate the transaction based on things that don't depend on the context
func validateBasic(tx StdTx) (err sdk.Error) {
// Assert that there are signatures.
sigs := tx.GetSignatures()
if len(sigs) == 0 {
return sdk.ErrUnauthorized("no signers")
}
// Assert that number of signatures is correct.
var signerAddrs = tx.GetSigners()
if len(sigs) != len(signerAddrs) {
return sdk.ErrUnauthorized("wrong number of signers")
}
memo := tx.GetMemo()
if len(memo) > maxMemoCharacters {
return sdk.ErrMemoTooLarge(
fmt.Sprintf("maximum number of characters is %d but received %d characters",
maxMemoCharacters, len(memo)))
}
return nil
}
2018-03-03 23:32:39 -08:00
// verify the signature and increment the sequence.
2018-03-14 10:16:52 -07:00
// if the account doesn't have a pubkey, set it.
2018-03-17 11:54:18 -07:00
func processSig(
2018-05-23 19:26:54 -07:00
ctx sdk.Context, am AccountMapper,
addr sdk.AccAddress, sig StdSignature, signBytes []byte, simulate bool) (
2018-05-23 19:26:54 -07:00
acc Account, res sdk.Result) {
2018-03-17 11:54:18 -07:00
// Get the account.
2018-03-03 23:32:39 -08:00
acc = am.GetAccount(ctx, addr)
if acc == nil {
2018-03-17 19:42:54 -07:00
return nil, sdk.ErrUnknownAddress(addr.String()).Result()
2018-03-03 23:32:39 -08:00
}
accnum := acc.GetAccountNumber()
2018-03-03 23:32:39 -08:00
seq := acc.GetSequence()
// Check account number.
if accnum != sig.AccountNumber {
return nil, sdk.ErrInvalidSequence(
fmt.Sprintf("Invalid account number. Got %d, expected %d", sig.AccountNumber, accnum)).Result()
}
// Check sequence number.
if seq != sig.Sequence {
return nil, sdk.ErrInvalidSequence(
fmt.Sprintf("Invalid sequence. Got %d, expected %d", sig.Sequence, seq)).Result()
2018-03-03 23:32:39 -08:00
}
err := acc.SetSequence(seq + 1)
if err != nil {
// Handle w/ #870
panic(err)
}
pubKey, res := processPubKey(acc, sig, simulate)
if !res.IsOK() {
return nil, res
}
err = acc.SetPubKey(pubKey)
if err != nil {
return nil, sdk.ErrInternal("setting PubKey on signer's account").Result()
}
consumeSignatureVerificationGas(ctx.GasMeter(), pubKey)
if !simulate && !pubKey.VerifyBytes(signBytes, sig.Signature) {
return nil, sdk.ErrUnauthorized("signature verification failed").Result()
}
return
}
func processPubKey(acc Account, sig StdSignature, simulate bool) (crypto.PubKey, sdk.Result) {
2018-03-14 10:16:52 -07:00
// If pubkey is not known for account,
2018-03-17 11:54:18 -07:00
// set it from the StdSignature.
2018-03-03 23:32:39 -08:00
pubKey := acc.GetPubKey()
if simulate {
// In simulate mode the transaction comes with no signatures, thus
// if the account's pubkey is nil, both signature verification
// and gasKVStore.Set() shall consume the largest amount, i.e.
// it takes more gas to verifiy secp256k1 keys than ed25519 ones.
if pubKey == nil {
return secp256k1.GenPrivKey().PubKey(), sdk.Result{}
}
return pubKey, sdk.Result{}
}
2018-04-06 17:25:08 -07:00
if pubKey == nil {
2018-03-03 23:32:39 -08:00
pubKey = sig.PubKey
2018-04-06 17:25:08 -07:00
if pubKey == nil {
2018-03-17 11:54:18 -07:00
return nil, sdk.ErrInvalidPubKey("PubKey not found").Result()
}
if !bytes.Equal(pubKey.Address(), acc.GetAddress()) {
2018-03-17 11:54:18 -07:00
return nil, sdk.ErrInvalidPubKey(
fmt.Sprintf("PubKey does not match Signer address %v", acc.GetAddress())).Result()
2018-03-14 10:16:52 -07:00
}
2018-03-03 23:32:39 -08:00
}
return pubKey, sdk.Result{}
2018-03-17 11:54:18 -07:00
}
func consumeSignatureVerificationGas(meter sdk.GasMeter, pubkey crypto.PubKey) {
switch pubkey.(type) {
case ed25519.PubKeyEd25519:
meter.ConsumeGas(ed25519VerifyCost, "ante verify: ed25519")
case secp256k1.PubKeySecp256k1:
meter.ConsumeGas(secp256k1VerifyCost, "ante verify: secp256k1")
default:
panic("Unrecognized signature type")
}
}
2018-03-17 13:53:27 -07:00
// Deduct the fee from the account.
// We could use the CoinKeeper (in addition to the AccountMapper,
// because the CoinKeeper doesn't give us accounts), but it seems easier to do this.
2018-05-23 19:26:54 -07:00
func deductFees(acc Account, fee StdFee) (Account, sdk.Result) {
2018-03-17 11:54:18 -07:00
coins := acc.GetCoins()
feeAmount := fee.Amount
2018-03-17 13:20:24 -07:00
2018-03-17 11:54:18 -07:00
newCoins := coins.Minus(feeAmount)
if !newCoins.IsNotNegative() {
errMsg := fmt.Sprintf("%s < %s", coins, feeAmount)
return nil, sdk.ErrInsufficientFunds(errMsg).Result()
2018-03-14 10:16:52 -07:00
}
err := acc.SetCoins(newCoins)
if err != nil {
// Handle w/ #870
panic(err)
}
2018-03-17 11:54:18 -07:00
return acc, sdk.Result{}
2018-03-03 23:32:39 -08:00
}
2018-04-16 16:06:07 -07:00
// BurnFeeHandler burns all fees (decreasing total supply)
func BurnFeeHandler(_ sdk.Context, _ sdk.Tx, _ sdk.Coins) {}