2018-01-12 11:49:53 -08:00
|
|
|
package auth
|
|
|
|
|
|
|
|
import (
|
2018-03-17 11:54:18 -07:00
|
|
|
"bytes"
|
2018-09-08 01:55:49 -07:00
|
|
|
"encoding/hex"
|
2018-03-12 17:40:04 -07:00
|
|
|
"fmt"
|
2018-11-15 06:30:24 -08:00
|
|
|
|
2018-08-21 07:13:34 -07:00
|
|
|
"github.com/tendermint/tendermint/crypto"
|
|
|
|
"github.com/tendermint/tendermint/crypto/ed25519"
|
|
|
|
"github.com/tendermint/tendermint/crypto/secp256k1"
|
2018-12-10 06:27:25 -08:00
|
|
|
|
|
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
2018-01-12 11:49:53 -08:00
|
|
|
)
|
|
|
|
|
2018-05-15 06:02:54 -07:00
|
|
|
const (
|
2018-12-10 02:48:19 -08:00
|
|
|
memoCostPerByte sdk.Gas = 3
|
|
|
|
ed25519VerifyCost = 590
|
|
|
|
secp256k1VerifyCost = 1000
|
|
|
|
maxMemoCharacters = 256
|
2018-12-04 08:42:15 -08:00
|
|
|
|
2018-09-26 11:34:01 -07:00
|
|
|
// how much gas = 1 atom
|
2018-12-10 02:48:19 -08:00
|
|
|
gasPerUnitCost = 10000
|
2018-12-04 08:42:15 -08:00
|
|
|
|
2018-11-15 06:30:24 -08:00
|
|
|
// max total number of sigs per tx
|
|
|
|
txSigLimit = 7
|
2018-05-15 06:02:54 -07:00
|
|
|
)
|
|
|
|
|
2018-12-04 08:42:15 -08:00
|
|
|
// NewAnteHandler returns an AnteHandler that checks and increments sequence
|
|
|
|
// numbers, checks signatures & account numbers, and deducts fees from the first
|
|
|
|
// signer.
|
2018-10-20 23:22:48 -07:00
|
|
|
func NewAnteHandler(am AccountKeeper, fck FeeCollectionKeeper) sdk.AnteHandler {
|
2018-01-12 11:49:53 -08:00
|
|
|
return func(
|
2018-08-25 12:12:14 -07:00
|
|
|
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-01-12 11:49:53 -08:00
|
|
|
|
2018-12-04 08:42:15 -08:00
|
|
|
// all transactions must be of type auth.StdTx
|
2018-05-23 19:26:54 -07:00
|
|
|
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-12-04 08:42:15 -08:00
|
|
|
// Ensure that the provided fees meet a minimum threshold for the validator,
|
|
|
|
// if this is a CheckTx. This is only for local mempool purposes, and thus
|
|
|
|
// is only ran on check tx.
|
2018-09-26 11:34:01 -07:00
|
|
|
if ctx.IsCheckTx() && !simulate {
|
2018-12-17 15:43:16 -08:00
|
|
|
res := EnsureSufficientMempoolFees(ctx, stdTx)
|
2018-09-26 11:34:01 -07:00
|
|
|
if !res.IsOK() {
|
|
|
|
return newCtx, res, true
|
|
|
|
}
|
2018-08-22 04:38:55 -07:00
|
|
|
}
|
2018-07-13 10:53:12 -07:00
|
|
|
|
2018-12-17 15:43:16 -08:00
|
|
|
newCtx = SetGasMeter(simulate, ctx, stdTx)
|
2018-09-26 11:34:01 -07:00
|
|
|
|
2018-12-04 08:42:15 -08: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 call.
|
2018-07-12 19:38:22 -07:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2018-11-21 02:16:56 -08:00
|
|
|
if err := tx.ValidateBasic(); err != nil {
|
2018-07-13 10:53:12 -07:00
|
|
|
return newCtx, err.Result(), true
|
2018-01-12 11:49:53 -08:00
|
|
|
}
|
2018-11-19 09:13:45 -08:00
|
|
|
|
2018-07-13 10:53:12 -07:00
|
|
|
newCtx.GasMeter().ConsumeGas(memoCostPerByte*sdk.Gas(len(stdTx.GetMemo())), "memo")
|
2018-01-12 11:49:53 -08:00
|
|
|
|
2018-12-18 12:41:54 -08:00
|
|
|
// stdSigs contains the sequence number, account number, and signatures.
|
|
|
|
// When simulating, this would just be a 0-length slice.
|
|
|
|
signerAddrs := stdTx.GetSigners()
|
|
|
|
signerAccs := make([]Account, len(signerAddrs))
|
|
|
|
isGenesis := ctx.BlockHeight() == 0
|
|
|
|
|
|
|
|
// fetch first signer, who's going to pay the fees
|
|
|
|
signerAccs[0], res = GetSignerAcc(newCtx, am, signerAddrs[0])
|
2018-09-26 11:34:01 -07:00
|
|
|
if !res.IsOK() {
|
|
|
|
return newCtx, res, true
|
|
|
|
}
|
|
|
|
if !stdTx.Fee.Amount.IsZero() {
|
2018-12-17 15:43:16 -08:00
|
|
|
signerAccs[0], res = DeductFees(signerAccs[0], stdTx.Fee)
|
2018-03-03 23:32:39 -08:00
|
|
|
if !res.IsOK() {
|
2018-07-13 10:53:12 -07:00
|
|
|
return newCtx, res, true
|
2018-01-12 11:49:53 -08:00
|
|
|
}
|
2018-12-04 08:42:15 -08:00
|
|
|
|
2018-10-19 11:36:00 -07:00
|
|
|
fck.AddCollectedFees(newCtx, stdTx.Fee.Amount)
|
2018-09-26 11:34:01 -07:00
|
|
|
}
|
2018-03-17 11:54:18 -07:00
|
|
|
|
2018-12-17 15:43:16 -08:00
|
|
|
// stdSigs contains the sequence number, account number, and signatures.
|
|
|
|
// When simulating, this would just be a 0-length slice.
|
|
|
|
stdSigs := stdTx.GetSignatures()
|
|
|
|
|
2018-09-26 11:34:01 -07:00
|
|
|
for i := 0; i < len(stdSigs); i++ {
|
2018-12-18 12:41:54 -08:00
|
|
|
// skip the fee payer, account is cached and fees were deducted already
|
|
|
|
if i != 0 {
|
|
|
|
signerAccs[i], res = GetSignerAcc(newCtx, am, signerAddrs[i])
|
|
|
|
if !res.IsOK() {
|
|
|
|
return newCtx, res, true
|
|
|
|
}
|
|
|
|
}
|
2018-09-26 11:34:01 -07:00
|
|
|
// check signature, return account with incremented nonce
|
2018-12-18 12:41:54 -08:00
|
|
|
signBytes := GetSignBytes(newCtx.ChainID(), stdTx, signerAccs[i], isGenesis)
|
|
|
|
signerAccs[i], res = processSig(newCtx, signerAccs[i], stdSigs[i], signBytes, simulate)
|
2018-09-26 11:34:01 -07:00
|
|
|
if !res.IsOK() {
|
|
|
|
return newCtx, res, true
|
2018-03-17 11:54:18 -07:00
|
|
|
}
|
|
|
|
|
2018-09-26 11:34:01 -07:00
|
|
|
am.SetAccount(newCtx, signerAccs[i])
|
2018-01-12 11:49:53 -08:00
|
|
|
}
|
|
|
|
|
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-07-13 10:53:12 -07:00
|
|
|
return newCtx, sdk.Result{GasWanted: stdTx.Fee.Gas}, false // continue...
|
2018-01-12 11:49:53 -08:00
|
|
|
}
|
|
|
|
}
|
2018-03-03 23:32:39 -08:00
|
|
|
|
2018-12-18 12:41:54 -08:00
|
|
|
// GetSignerAcc a signers for a given address that is expected to sign a transaction.
|
|
|
|
func GetSignerAcc(ctx sdk.Context, am AccountKeeper, addr sdk.AccAddress) (Account, sdk.Result) {
|
|
|
|
if acc := am.GetAccount(ctx, addr); acc != nil {
|
|
|
|
return acc, sdk.Result{}
|
2018-03-03 23:32:39 -08:00
|
|
|
}
|
2018-12-18 12:41:54 -08:00
|
|
|
return nil, sdk.ErrUnknownAddress(addr.String()).Result()
|
2018-09-26 11:34:01 -07:00
|
|
|
}
|
2018-03-03 23:32:39 -08:00
|
|
|
|
2018-12-04 08:42:15 -08:00
|
|
|
// verify the signature and increment the sequence. If the account doesn't have
|
|
|
|
// a pubkey, set it.
|
|
|
|
func processSig(
|
|
|
|
ctx sdk.Context, acc Account, sig StdSignature, signBytes []byte, simulate bool,
|
|
|
|
) (updatedAcc Account, res sdk.Result) {
|
|
|
|
|
2018-12-17 15:43:16 -08:00
|
|
|
pubKey, res := ProcessPubKey(acc, sig, simulate)
|
2018-08-31 10:04:11 -07:00
|
|
|
if !res.IsOK() {
|
|
|
|
return nil, res
|
|
|
|
}
|
2018-12-04 08:42:15 -08:00
|
|
|
|
2018-09-26 11:34:01 -07:00
|
|
|
err := acc.SetPubKey(pubKey)
|
2018-08-31 10:04:11 -07:00
|
|
|
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()
|
|
|
|
}
|
|
|
|
|
2018-09-26 11:34:01 -07:00
|
|
|
err = acc.SetSequence(acc.GetSequence() + 1)
|
|
|
|
if err != nil {
|
|
|
|
// Handle w/ #870
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return acc, res
|
2018-08-31 10:04:11 -07:00
|
|
|
}
|
|
|
|
|
2018-09-08 01:55:49 -07:00
|
|
|
var dummySecp256k1Pubkey secp256k1.PubKeySecp256k1
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
bz, _ := hex.DecodeString("035AD6810A47F073553FF30D2FCC7E0D3B1C0B74B61A1AAA2582344037151E143A")
|
|
|
|
copy(dummySecp256k1Pubkey[:], bz)
|
|
|
|
}
|
|
|
|
|
2018-12-17 15:43:16 -08:00
|
|
|
// ProcessPubKey verifies that the given account address matches that of the
|
|
|
|
// StdSignature. In addition, it will set the public key of the account if it
|
|
|
|
// has not been set.
|
|
|
|
func ProcessPubKey(acc Account, sig StdSignature, simulate bool) (crypto.PubKey, sdk.Result) {
|
2018-12-04 08:42:15 -08:00
|
|
|
// If pubkey is not known for account, set it from the StdSignature.
|
2018-03-03 23:32:39 -08:00
|
|
|
pubKey := acc.GetPubKey()
|
2018-08-31 10:04:11 -07:00
|
|
|
if simulate {
|
2018-12-04 08:42:15 -08:00
|
|
|
// 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 verify
|
|
|
|
// secp256k1 keys than ed25519 ones.
|
2018-08-31 10:04:11 -07:00
|
|
|
if pubKey == nil {
|
2018-09-08 01:55:49 -07:00
|
|
|
return dummySecp256k1Pubkey, sdk.Result{}
|
2018-08-31 10:04:11 -07:00
|
|
|
}
|
2018-12-04 08:42:15 -08:00
|
|
|
|
2018-08-31 10:04:11 -07:00
|
|
|
return pubKey, sdk.Result{}
|
|
|
|
}
|
2018-12-04 08:42:15 -08:00
|
|
|
|
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()
|
|
|
|
}
|
2018-12-04 08:42:15 -08:00
|
|
|
|
2018-08-31 10:04:11 -07:00
|
|
|
if !bytes.Equal(pubKey.Address(), acc.GetAddress()) {
|
2018-03-17 11:54:18 -07:00
|
|
|
return nil, sdk.ErrInvalidPubKey(
|
2018-08-31 10:04:11 -07:00
|
|
|
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
|
|
|
}
|
2018-12-04 08:42:15 -08:00
|
|
|
|
2018-08-31 10:04:11 -07:00
|
|
|
return pubKey, sdk.Result{}
|
2018-03-17 11:54:18 -07:00
|
|
|
}
|
2018-03-15 09:37:16 -07:00
|
|
|
|
2018-08-21 07:13:34 -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-11-19 09:13:45 -08:00
|
|
|
func adjustFeesByGas(fees sdk.Coins, gas uint64) sdk.Coins {
|
2018-09-26 11:34:01 -07:00
|
|
|
gasCost := gas / gasPerUnitCost
|
2018-09-19 08:25:52 -07:00
|
|
|
gasFees := make(sdk.Coins, len(fees))
|
2018-11-19 09:13:45 -08:00
|
|
|
|
2018-09-26 11:34:01 -07:00
|
|
|
// TODO: Make this not price all coins in the same way
|
2018-11-19 09:13:45 -08:00
|
|
|
// TODO: Undo int64 casting once unsigned integers are supported for coins
|
2018-09-19 08:25:52 -07:00
|
|
|
for i := 0; i < len(fees); i++ {
|
2018-11-19 09:13:45 -08:00
|
|
|
gasFees[i] = sdk.NewInt64Coin(fees[i].Denom, int64(gasCost))
|
2018-09-19 08:25:52 -07:00
|
|
|
}
|
2018-11-19 09:13:45 -08:00
|
|
|
|
2018-09-19 08:25:52 -07:00
|
|
|
return fees.Plus(gasFees)
|
|
|
|
}
|
|
|
|
|
2018-12-17 15:43:16 -08:00
|
|
|
// DeductFees deducts fees from the given account.
|
|
|
|
//
|
|
|
|
// NOTE: We could use the CoinKeeper (in addition to the AccountKeeper, because
|
|
|
|
// the CoinKeeper doesn't give us accounts), but it seems easier to do this.
|
|
|
|
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-11-20 01:22:35 -08:00
|
|
|
if !feeAmount.IsValid() {
|
|
|
|
return nil, sdk.ErrInsufficientFee(fmt.Sprintf("invalid fee amount: %s", feeAmount)).Result()
|
|
|
|
}
|
|
|
|
|
|
|
|
newCoins, ok := coins.SafeMinus(feeAmount)
|
|
|
|
if ok {
|
2018-03-17 11:54:18 -07:00
|
|
|
errMsg := fmt.Sprintf("%s < %s", coins, feeAmount)
|
|
|
|
return nil, sdk.ErrInsufficientFunds(errMsg).Result()
|
2018-03-14 10:16:52 -07:00
|
|
|
}
|
2018-11-20 01:22:35 -08:00
|
|
|
|
2018-06-28 15:52:10 -07:00
|
|
|
err := acc.SetCoins(newCoins)
|
|
|
|
if err != nil {
|
|
|
|
// Handle w/ #870
|
|
|
|
panic(err)
|
|
|
|
}
|
2018-11-20 01:22:35 -08:00
|
|
|
|
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
|
|
|
|
2018-12-17 15:43:16 -08:00
|
|
|
// EnsureSufficientMempoolFees verifies that the given transaction has supplied
|
|
|
|
// enough fees to cover a proposer's minimum fees. An result object is returned
|
|
|
|
// indicating success or failure.
|
|
|
|
//
|
|
|
|
// NOTE: This should only be called during CheckTx as it cannot be part of
|
|
|
|
// consensus.
|
|
|
|
func EnsureSufficientMempoolFees(ctx sdk.Context, stdTx StdTx) sdk.Result {
|
2018-12-04 08:42:15 -08:00
|
|
|
// Currently we use a very primitive gas pricing model with a constant
|
|
|
|
// gasPrice where adjustFeesByGas handles calculating the amount of fees
|
|
|
|
// required based on the provided gas.
|
2018-11-20 01:22:35 -08:00
|
|
|
//
|
|
|
|
// TODO:
|
|
|
|
// - Make the gasPrice not a constant, and account for tx size.
|
|
|
|
// - Make Gas an unsigned integer and use tx basic validation
|
|
|
|
if stdTx.Fee.Gas <= 0 {
|
|
|
|
return sdk.ErrInternal(fmt.Sprintf("invalid gas supplied: %d", stdTx.Fee.Gas)).Result()
|
|
|
|
}
|
2018-11-21 02:16:56 -08:00
|
|
|
requiredFees := adjustFeesByGas(ctx.MinimumFees(), stdTx.Fee.Gas)
|
2018-09-26 11:34:01 -07:00
|
|
|
|
2018-11-07 00:14:48 -08:00
|
|
|
// NOTE: !A.IsAllGTE(B) is not the same as A.IsAllLT(B).
|
|
|
|
if !ctx.MinimumFees().IsZero() && !stdTx.Fee.Amount.IsAllGTE(requiredFees) {
|
2018-09-26 11:34:01 -07:00
|
|
|
// validators reject any tx from the mempool with less than the minimum fee per gas * gas factor
|
2018-12-04 08:42:15 -08:00
|
|
|
return sdk.ErrInsufficientFee(
|
|
|
|
fmt.Sprintf(
|
|
|
|
"insufficient fee, got: %q required: %q", stdTx.Fee.Amount, requiredFees),
|
|
|
|
).Result()
|
2018-09-26 11:34:01 -07:00
|
|
|
}
|
2018-12-04 08:42:15 -08:00
|
|
|
|
2018-09-26 11:34:01 -07:00
|
|
|
return sdk.Result{}
|
|
|
|
}
|
|
|
|
|
2018-12-17 15:43:16 -08:00
|
|
|
// SetGasMeter returns a new context with a gas meter set from a given context.
|
|
|
|
func SetGasMeter(simulate bool, ctx sdk.Context, stdTx StdTx) sdk.Context {
|
2018-11-19 09:13:45 -08:00
|
|
|
// In various cases such as simulation and during the genesis block, we do not
|
|
|
|
// meter any gas utilization.
|
2018-10-19 11:00:27 -07:00
|
|
|
if simulate || ctx.BlockHeight() == 0 {
|
2018-09-26 11:34:01 -07:00
|
|
|
return ctx.WithGasMeter(sdk.NewInfiniteGasMeter())
|
|
|
|
}
|
2018-11-19 09:13:45 -08:00
|
|
|
|
2018-09-26 11:34:01 -07:00
|
|
|
return ctx.WithGasMeter(sdk.NewGasMeter(stdTx.Fee.Gas))
|
|
|
|
}
|
|
|
|
|
2018-12-18 12:41:54 -08:00
|
|
|
// GetSignBytes returns a slice of bytes to sign over for a given transaction
|
|
|
|
// and an account.
|
|
|
|
func GetSignBytes(chainID string, stdTx StdTx, acc Account, genesis bool) []byte {
|
|
|
|
accNum := acc.GetAccountNumber()
|
|
|
|
if genesis {
|
|
|
|
accNum = 0
|
2018-09-26 11:34:01 -07:00
|
|
|
}
|
2018-12-18 12:41:54 -08:00
|
|
|
return StdSignBytes(chainID,
|
|
|
|
accNum, acc.GetSequence(),
|
|
|
|
stdTx.Fee, stdTx.Msgs, stdTx.Memo)
|
2018-09-26 11:34:01 -07:00
|
|
|
}
|