cosmos-sdk/x/auth/ante.go

151 lines
4.0 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"
)
2018-03-14 10:16:52 -07:00
// NewAnteHandler returns an AnteHandler that checks
// and increments sequence numbers, checks signatures,
// and deducts fees from the first signer.
func NewAnteHandler(accountMapper sdk.AccountMapper) sdk.AnteHandler {
return func(
2018-01-12 14:30:02 -08:00
ctx sdk.Context, tx sdk.Tx,
2018-01-26 05:11:01 -08:00
) (_ sdk.Context, _ sdk.Result, abort bool) {
2018-01-26 06:22:56 -08:00
// Assert that there are signatures.
2018-03-03 23:32:39 -08:00
var sigs = tx.GetSignatures()
2018-02-17 13:19:34 -08:00
if len(sigs) == 0 {
return ctx,
sdk.ErrUnauthorized("no signers").Result(),
true
}
2018-01-26 04:19:33 -08:00
2018-03-03 23:32:39 -08:00
// TODO: can tx just implement message?
msg := tx.GetMsg()
2018-01-26 06:22:56 -08:00
// TODO: will this always be a stdtx? should that be used in the function signature?
stdTx, ok := tx.(sdk.StdTx)
if !ok {
return ctx, sdk.ErrInternal("tx must be sdk.StdTx").Result(), true
}
2018-01-26 04:19:33 -08:00
// Assert that number of signatures is correct.
2018-03-03 23:32:39 -08:00
var signerAddrs = msg.GetSigners()
2018-02-17 13:19:34 -08:00
if len(sigs) != len(signerAddrs) {
return ctx,
sdk.ErrUnauthorized("wrong number of signers").Result(),
true
}
2018-03-17 11:54:18 -07:00
// Get the sign bytes (requires all sequence numbers and the fee)
sequences := make([]int64, len(signerAddrs))
for i := 0; i < len(signerAddrs); i++ {
sequences[i] = sigs[i].Sequence
}
2018-03-17 11:54:18 -07:00
fee := stdTx.Fee
signBytes := sdk.StdSignBytes(ctx.ChainID(), sequences, fee, msg)
2018-03-04 00:15:26 -08:00
2018-03-14 10:16:52 -07:00
// Check sig and nonce and collect signer accounts.
var signerAccs = make([]sdk.Account, len(signerAddrs))
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
signerAcc, res := processSig(
ctx, accountMapper,
signerAddr, sig, signBytes,
)
2018-03-03 23:32:39 -08:00
if !res.IsOK() {
return ctx, res, true
}
2018-03-17 11:54:18 -07:00
// first sig pays the fees
if i == 0 {
2018-03-17 13:20:24 -07:00
// TODO: min fee
if !fee.Amount.IsZero() {
signerAcc, res = deductFees(signerAcc, fee)
if !res.IsOK() {
return ctx, res, true
}
2018-03-17 11:54:18 -07:00
}
}
// Save the account.
accountMapper.SetAccount(ctx, 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
ctx = WithSigners(ctx, 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-01-12 14:30:02 -08:00
return ctx, sdk.Result{}, false // continue...
}
}
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(
ctx sdk.Context, am sdk.AccountMapper,
addr sdk.Address, sig sdk.StdSignature, signBytes []byte) (
acc sdk.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 11:54:18 -07:00
return nil, sdk.ErrUnrecognizedAddress(addr.String()).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
}
acc.SetSequence(seq + 1)
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 pubKey.Empty() {
pubKey = sig.PubKey
2018-03-14 10:16:52 -07:00
if pubKey.Empty() {
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-03-03 23:32:39 -08:00
err := acc.SetPubKey(pubKey)
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
}
}
// Check sig.
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-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-03-17 11:54:18 -07:00
func deductFees(acc sdk.Account, fee sdk.StdFee) (sdk.Account, sdk.Result) {
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-03-17 11:54:18 -07:00
acc.SetCoins(newCoins)
return acc, sdk.Result{}
2018-03-03 23:32:39 -08:00
}