2018-01-12 11:49:53 -08:00
|
|
|
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"
|
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-01-12 11:49:53 -08:00
|
|
|
)
|
|
|
|
|
2018-05-15 06:02:54 -07:00
|
|
|
const (
|
2018-08-21 07:13:34 -07:00
|
|
|
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
|
2018-06-11 19:30:54 -07:00
|
|
|
// 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
|
|
|
|
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-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
|
2018-08-25 12:12:14 -07:00
|
|
|
if simulate {
|
2018-08-22 04:38:55 -07:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2018-07-07 19:34:46 -07:00
|
|
|
err := validateBasic(stdTx)
|
|
|
|
if err != nil {
|
2018-07-13 10:53:12 -07:00
|
|
|
return newCtx, err.Result(), true
|
2018-01-12 11:49:53 -08:00
|
|
|
}
|
2018-01-26 04:19:33 -08:00
|
|
|
|
2018-07-07 19:34:46 -07:00
|
|
|
sigs := stdTx.GetSignatures()
|
|
|
|
signerAddrs := stdTx.GetSigners()
|
|
|
|
msgs := tx.GetMsgs()
|
2018-06-20 12:27:36 -07:00
|
|
|
|
|
|
|
// charge gas for the memo
|
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-06-11 19:30:54 -07:00
|
|
|
// Get the sign bytes (requires all account & sequence numbers and the fee)
|
2018-07-07 19:34:46 -07:00
|
|
|
sequences := make([]int64, len(sigs))
|
|
|
|
accNums := make([]int64, len(sigs))
|
|
|
|
for i := 0; i < len(sigs); i++ {
|
2018-03-10 15:57:39 -08:00
|
|
|
sequences[i] = sigs[i].Sequence
|
2018-06-11 19:30:54 -07:00
|
|
|
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())
|
2018-03-17 11:54:18 -07:00
|
|
|
signerAcc, res := processSig(
|
2018-07-13 10:53:12 -07:00
|
|
|
newCtx, am,
|
2018-03-17 11:54:18 -07:00
|
|
|
signerAddr, sig, signBytes,
|
|
|
|
)
|
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-03-17 11:54:18 -07:00
|
|
|
|
|
|
|
// first sig pays the fees
|
2018-07-07 19:34:46 -07:00
|
|
|
// 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")
|
2018-07-07 19:34:46 -07:00
|
|
|
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-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-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-01-12 11:49:53 -08:00
|
|
|
}
|
|
|
|
}
|
2018-03-03 23:32:39 -08:00
|
|
|
|
2018-07-07 19:34:46 -07: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,
|
2018-07-06 00:06:53 -07:00
|
|
|
addr sdk.AccAddress, sig StdSignature, signBytes []byte) (
|
2018-05-23 19:26:54 -07:00
|
|
|
acc Account, res sdk.Result) {
|
2018-03-03 23:32:39 -08:00
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2018-06-11 19:30:54 -07:00
|
|
|
// Check account number.
|
|
|
|
accnum := acc.GetAccountNumber()
|
|
|
|
if accnum != sig.AccountNumber {
|
|
|
|
return nil, sdk.ErrInvalidSequence(
|
|
|
|
fmt.Sprintf("Invalid account number. Got %d, expected %d", sig.AccountNumber, accnum)).Result()
|
|
|
|
}
|
|
|
|
|
2018-03-03 23:32:39 -08:00
|
|
|
// Check and increment sequence number.
|
|
|
|
seq := acc.GetSequence()
|
|
|
|
if seq != sig.Sequence {
|
2018-03-12 17:40:04 -07:00
|
|
|
return nil, sdk.ErrInvalidSequence(
|
|
|
|
fmt.Sprintf("Invalid sequence. Got %d, expected %d", sig.Sequence, seq)).Result()
|
2018-03-03 23:32:39 -08:00
|
|
|
}
|
2018-06-28 15:52:10 -07:00
|
|
|
err := acc.SetSequence(seq + 1)
|
|
|
|
if err != nil {
|
|
|
|
// Handle w/ #870
|
|
|
|
panic(err)
|
|
|
|
}
|
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()
|
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(), addr) {
|
|
|
|
return nil, sdk.ErrInvalidPubKey(
|
|
|
|
fmt.Sprintf("PubKey does not match Signer address %v", addr)).Result()
|
2018-03-14 10:16:52 -07:00
|
|
|
}
|
2018-06-28 15:52:10 -07:00
|
|
|
err = acc.SetPubKey(pubKey)
|
2018-03-03 23:32:39 -08:00
|
|
|
if err != nil {
|
2018-03-17 11:54:18 -07:00
|
|
|
return nil, sdk.ErrInternal("setting PubKey on signer's account").Result()
|
2018-03-03 23:32:39 -08:00
|
|
|
}
|
|
|
|
}
|
2018-03-17 15:09:04 -07:00
|
|
|
|
2018-03-03 23:32:39 -08:00
|
|
|
// Check sig.
|
2018-08-21 07:13:34 -07:00
|
|
|
consumeSignatureVerificationGas(ctx.GasMeter(), pubKey)
|
2018-03-16 17:57:28 -07:00
|
|
|
if !pubKey.VerifyBytes(signBytes, sig.Signature) {
|
2018-03-04 00:15:26 -08:00
|
|
|
return nil, sdk.ErrUnauthorized("signature verification failed").Result()
|
2018-03-03 23:32:39 -08:00
|
|
|
}
|
|
|
|
|
2018-03-17 11:54:18 -07:00
|
|
|
return
|
|
|
|
}
|
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-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
|
|
|
}
|
2018-06-28 15:52:10 -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)
|
2018-05-20 14:39:04 -07:00
|
|
|
func BurnFeeHandler(_ sdk.Context, _ sdk.Tx, _ sdk.Coins) {}
|