cosmos-sdk/docs/core/app3.md

245 lines
8.2 KiB
Markdown
Raw Normal View History

2018-06-26 15:05:12 -07:00
# Authentication
2018-06-16 19:24:48 -07:00
2018-06-26 15:05:12 -07:00
In the previous app, we introduced a new `Msg` type and used Amino to encode
transactions. In that example, our `Tx` implementation was still just a simple
wrapper of the `Msg`, providing no actual authentication. Here, in `App3`, we
expand on `App2` to provide real authentication in the transactions.
2018-06-16 19:24:48 -07:00
2018-06-26 18:29:54 -07:00
Without loss of generality, the SDK prescribes native
account and transaction types that are sufficient for a wide range of applications.
These are implemented in the `x/auth` module, where
all authentication related data structures and logic reside.
Applications that use `x/auth` don't need to worry about any of the details of
authentication and replay protection, as they are handled automatically. For
completeness, we will explain everything here.
## Account
The `Account` interface provides a model of accounts that have:
- Address for identification
- PubKey for authentication
- AccountNumber to prune empty accounts
- Sequence to prevent transaction replays
- Coins to carry a balance
It consists of getters and setters for each of these:
```go
// Account is a standard account using a sequence number for replay protection
// and a pubkey for authentication.
type Account interface {
GetAddress() sdk.Address
SetAddress(sdk.Address) error // errors if already set.
GetPubKey() crypto.PubKey // can return nil.
SetPubKey(crypto.PubKey) error
GetAccountNumber() int64
SetAccountNumber(int64) error
GetSequence() int64
SetSequence(int64) error
GetCoins() sdk.Coins
SetCoins(sdk.Coins) error
}
```
## BaseAccount
The default implementation of `Account` is the `BaseAccount`:
```go
// BaseAccount - base account structure.
// Extend this by embedding this in your AppAccount.
// See the examples/basecoin/types/account.go for an example.
type BaseAccount struct {
Address sdk.Address `json:"address"`
Coins sdk.Coins `json:"coins"`
PubKey crypto.PubKey `json:"public_key"`
AccountNumber int64 `json:"account_number"`
Sequence int64 `json:"sequence"`
}
```
It simply contains a field for each of the methods.
The `Address`, `PubKey`, and `AccountNumber` of the `BaseAccpunt` cannot be changed once they are set.
The `Sequence` increments by one with every transaction. This ensures that a
given transaction can only be executed once, as the `Sequence` contained in the
transaction must match that contained in the account.
The `Coins` will change according to the logic of each transaction type.
If the `Coins` are ever emptied, the account will be deleted from the store. If
coins are later sent to the same `Address`, the account will be recreated but
with a new `AccountNumber`. This allows us to prune empty accounts from the
store, while still preventing transaction replay if accounts become non-empty
again in the future.
2018-06-26 15:05:12 -07:00
## StdTx
2018-06-16 19:24:48 -07:00
2018-06-26 18:29:54 -07:00
The standard way to create a transaction from a message is to use the `StdTx` struct defined in the `x/auth` module:
2018-06-16 19:24:48 -07:00
```go
2018-06-26 18:29:54 -07:00
// StdTx is a standard way to wrap a Msg with Fee and Signatures.
// NOTE: the first signature is the FeePayer (Signatures must not be nil).
2018-06-16 19:24:48 -07:00
type StdTx struct {
2018-06-26 18:29:54 -07:00
Msgs []sdk.Msg `json:"msg"`
2018-06-16 19:24:48 -07:00
Fee StdFee `json:"fee"`
Signatures []StdSignature `json:"signatures"`
2018-06-26 18:29:54 -07:00
Memo string `json:"memo"`
2018-06-16 19:24:48 -07:00
}
```
2018-06-26 18:29:54 -07:00
The `StdTx` includes a list of messages, information about the fee being paid,
and a list of signatures. It also includes an optional `Memo` for additional
data. Note that the list of signatures must match the result of `GetSigners()`
for each `Msg`!
The signatures are provided in a standard form as `StdSignature`:
2018-06-16 19:24:48 -07:00
```go
2018-06-26 18:29:54 -07:00
// StdSignature wraps the Signature and includes counters for replay protection.
// It also includes an optional public key, which must be provided at least in
// the first transaction made by the account.
2018-06-16 19:24:48 -07:00
type StdSignature struct {
2018-06-26 18:29:54 -07:00
crypto.PubKey `json:"pub_key"` // optional
crypto.Signature `json:"signature"`
AccountNumber int64 `json:"account_number"`
Sequence int64 `json:"sequence"`
2018-06-16 19:24:48 -07:00
}
```
2018-06-26 18:29:54 -07:00
Recall that the `Sequence` is expected to increment every time a
message is signed by a given account in order to prevent "replay attacks" where
the same message could be executed over and over again. The `AccountNumber` is
assigned when the account is created or recreated after being emptied.
2018-06-16 19:24:48 -07:00
The `StdSignature` can also optionally include the public key for verifying the
2018-06-26 18:29:54 -07:00
signature. The public key only needs to be included the first time a transaction
is sent from a given account - from then on it will be stored in the `Account`
and can be left out of transactions.
2018-06-16 19:24:48 -07:00
2018-06-26 18:29:54 -07:00
The fee is provided in a standard form as `StdFee`:
2018-06-16 19:24:48 -07:00
```go
// StdFee includes the amount of coins paid in fees and the maximum
// gas to be used by the transaction. The ratio yields an effective "gasprice",
// which must be above some miminum to be accepted into the mempool.
type StdFee struct {
Amount sdk.Coins `json:"amount"`
Gas int64 `json:"gas"`
}
```
2018-06-26 18:29:54 -07:00
Note that the address responsible for paying the transactions fee is the first address
returned by msg.GetSigners() for the first `Msg`. The convenience function `FeePayer(tx Tx)` is provided
to return this.
## Signing
The standard bytes for signers to sign over is provided by:
```go
TODO
```
2018-06-16 19:24:48 -07:00
## AnteHandler
The AnteHandler is used to do all transaction-level processing (i.e. Fee payment, signature verification)
before passing the message to its respective handler.
```go
type AnteHandler func(ctx Context, tx Tx) (newCtx Context, result Result, abort bool)
```
The antehandler takes a Context and a transaction and returns a new Context, a Result, and the abort boolean.
As with the handler, all information necessary for processing a message should be available in the
context.
If the transaction fails, then the application should not waste time processing the message. Thus, the antehandler should
return an Error's Result method and set the abort boolean to `true` so that the application knows not to process the message in a handler.
Most applications can use the provided antehandler implementation in `x/auth` which handles signature verification
as well as collecting fees.
Note: Signatures must be over `auth.StdSignDoc` introduced above to use the provided antehandler.
```go
// File: cosmos-sdk/examples/basecoin/app/app.go
app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper, app.feeCollectionKeeper))
```
### Handling Fee payment
### Handling Authentication
The antehandler is responsible for handling all authentication of a transaction before passing the message onto its handler.
This generally involves signature verification. The antehandler should check that all of the addresses that are returned in
`tx.GetMsg().GetSigners()` signed the message and that they signed over `tx.GetMsg().GetSignBytes()`.
2018-06-26 14:21:46 -07:00
# Accounts
### auth.Account
### auth.AccountMapper
```go
// This AccountMapper encodes/decodes accounts using the
// go-amino (binary) encoding/decoding library.
type AccountMapper struct {
// The (unexposed) key used to access the store from the Context.
key sdk.StoreKey
// The prototypical Account concrete type.
proto Account
// The wire codec for binary encoding/decoding of accounts.
cdc *wire.Codec
}
```
The AccountMapper is responsible for managing and storing the state of all accounts in the application.
Example Initialization:
```go
// File: examples/basecoin/app/app.go
// Define the accountMapper.
app.accountMapper = auth.NewAccountMapper(
cdc,
app.keyAccount, // target store
&types.AppAccount{}, // prototype
)
```
The accountMapper allows you to retrieve the current account state by `GetAccount(ctx Context, addr auth.Address)` and change the state by
`SetAccount(ctx Context, acc Account)`.
Note: To update an account you will first have to get the account, update the appropriate fields with its associated setter method, and then call
`SetAccount(ctx Context, acc updatedAccount)`.
Updating accounts is made easier by using the `Keeper` struct in the `x/bank` module.
Example Initialization:
```go
// File: examples/basecoin/app/app.go
app.coinKeeper = bank.NewKeeper(app.accountMapper)
```
Example Usage:
```go
// Finds account with addr in accountmapper
// Adds coins to account's coin array
// Sets updated account in accountmapper
app.coinKeeper.AddCoins(ctx, addr, coins)
```