move AnteHandler to app2 and some cleanup in app3

This commit is contained in:
Ethan Buchman 2018-06-26 22:49:13 -04:00
parent 41c61d2cc7
commit 354f9760f8
3 changed files with 124 additions and 131 deletions

View File

@ -22,10 +22,10 @@ NOTE: This documentation is a work-in-progress!
- [Ante Handler](core/app2.md#ante-handler) - The AnteHandler
authenticates transactions
- [App3 - Modules](core/app3.md)
- [Account](core/app3.md#account) - Accounts are the prototypical object kept in the store
- [StdTx](core/app3.md#stdtx) - Transactions wrap messages and provide authentication
- [AccountMapper](core/app3.md#account-mapper) - AccountMapper
- [Accounts](core/app3.md#accounts) - Accounts are the prototypical object kept in the store
provides Account lookup on a KVStore
- [Transactions](core/app3.md#transactions) - `StdTx` is the default
implementation of `Tx`
- [CoinKeeper](core/app3.md#coin-keeper) - CoinKeeper allows for coin
transfer on an underlying AccountMapper
- [App4 - Validator Set Changes](core/app4.md)

View File

@ -1,19 +1,15 @@
# Amino
# Transactions
In the previous app we build a simple `bank` with one message type for sending
In the previous app we built a simple `bank` with one message type for sending
coins and one store for storing accounts.
Here we build `App2`, which expands on `App1` by introducing another message type for issuing new coins, and another store
for storing information about who can issue coins and how many.
Here we build `App2`, which expands on `App1` by introducing
`App2` will allow us to better demonstrate the security model of the SDK,
using object-capability keys to determine which handlers can access which
stores.
- a new message type for issuing new coins
- a new store for coin metadata (like who can issue coins)
- a requirement that transactions include valid signatures
Having multiple implementations of `Msg` also requires a better transaction
decoder, since we won't know before hand which type is contained in the
serialized `Tx`. In effect, we'd like to unmarshal directly into the `Msg`
interface, but there's no standard way to unmarshal into interfaces in Go.
This is what Amino is for :)
Along the way, we'll be introduced to Amino for encoding and decoding
transactions and to the AnteHandler for processing them.
## Message
@ -32,20 +28,17 @@ We'll need a new handler to support the new message type:
TODO
```
## BaseApp
```go
TODO
```
## Amino
The SDK is flexible about serialization - application developers can use any
serialization scheme to encode transactions and state. However, the SDK provides
a native serialization format called
[Amino](https://github.com/tendermint/go-amino).
Now that we have two implementations of `Msg`, we won't know before hand
which type is contained in a serialized `Tx`. Ideally, we would use the
`Msg` interface inside our `Tx` implementation, but the JSON decoder can't
decode into interface types. In fact, there's no standard way to unmarshal
into interfaces in Go. This is one of the primary reasons we built
[Amino](https://github.com/tendermint/go-amino) :).
The goal of Amino is to improve over the latest version of Protocol Buffers,
While SDK developers can encode transactions and state objects however they
like, Amino is the recommended format. The goal of Amino is to improve over the latest version of Protocol Buffers,
`proto3`. To that end, Amino is compatible with the subset of `proto3` that
excludes the `oneof` keyword.
@ -74,3 +67,63 @@ cdc := wire.NewCodec()
cdc.RegisterConcrete(MsgSend{}, "cosmos-sdk/Send", nil)
cdc.RegisterConcrete(MsgIssue{}, "cosmos-sdk/Issue", nil)
```
TODO: JSON, types table
## Tx
TODO
## AnteHandler
Now that we have an implementation of `Tx` that includes more than just the Msgs,
we need to specify how that extra information is validated and processed. This
is the role of the `AnteHandler`. The word `ante` here denotes "before", as the
`AnteHandler` is run before a `Handler`. While an app may have many Handlers,
one for each set of messages, it may have only a single `AnteHandler` that
corresponds to its single implementation of `Tx`.
The AnteHandler resembles a Handler:
```go
type AnteHandler func(ctx Context, tx Tx) (newCtx Context, result Result, abort bool)
```
Like Handler, AnteHandler takes a Context that restricts its access to stores
according to whatever capability keys it was granted. Instead of a `Msg`,
however, it takes a `Tx`.
Like Handler, AnteHandler returns a `Result` type, but it also returns a new
`Context` and an `abort bool`. TODO explain (do we still need abort? )
For `App2`, we simply check if the PubKey matches the Address, and the Signature validates with the PubKey:
```go
TODO
```
## App2
Let's put it all together now to get App2:
```go
TODO
```
## Conclusion
We've expanded on our first app by adding a new message type for issuing coins,
and by checking signatures. We learned how to use Amino for decoding into
interface types, allowing us to support multiple Msg types, and we learned how
to use the AnteHandler to validate transactions.
Unfortunately, our application is still insecure, because any valid transaction
can be replayed multiple times to drain someones account! Besides, validating
signatures and preventing replays aren't things developers should have to think
about.
In the next section, we introduce the built-in SDK modules `auth` and `bank`,
which respectively provide secure implementations for all our transaction authentication
and coin transfering needs.

View File

@ -1,21 +1,31 @@
# Authentication
# Modules
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.
transactions. We also introduced additional data to the `Tx`, and used a simple
`AnteHandler` to validate it.
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.
Here, in `App3`, we introduce two built-in SDK modules to
replace the `Msg`, `Tx`, `Handler`, and `AnteHandler` implementations we've seen
so far.
## Account
The `x/auth` module implements `Tx` and `AnteHandler - it has everything we need to
authenticate transactions. It also includes a new `Account` type that simplifies
working with accounts in the store.
The `Account` interface provides a model of accounts that have:
The `x/bank` module implements `Msg` and `Handler` - it has everything we need
to transfer coins between accounts.
Applications that use `x/auth` and `x/bank` thus significantly reduce the amount
of work they have to do so they can focus on their application specific logic in
their own modules.
Here, we'll introduce the important types from `x/auth` and `x/bank`, and show
how to make `App3` by using them. The complete code can be found in [app3.go](examples/app3.go).
## Accounts
The `x/auth` module defines a model of accounts much like Ethereum.
In this model, an account contains:
- Address for identification
- PubKey for authentication
@ -23,7 +33,9 @@ The `Account` interface provides a model of accounts that have:
- Sequence to prevent transaction replays
- Coins to carry a balance
It consists of getters and setters for each of these:
### Account
The `Account` interface captures this account model with getters and setters:
```go
// Account is a standard account using a sequence number for replay protection
@ -46,7 +58,11 @@ type Account interface {
}
```
## BaseAccount
Note this is a low-level interface - it allows any of the fields to be over
written. As we'll soon see, access can be restricted using the `Keeper`
paradigm.
### BaseAccount
The default implementation of `Account` is the `BaseAccount`:
@ -80,8 +96,14 @@ store, while still preventing transaction replay if accounts become non-empty
again in the future.
### AccountMapper
## StdTx
TODO
## Transaction
### StdTx
The standard way to create a transaction from a message is to use the `StdTx` struct defined in the `x/auth` module:
@ -141,7 +163,7 @@ Note that the address responsible for paying the transactions fee is the first a
returned by msg.GetSigners() for the first `Msg`. The convenience function `FeePayer(tx Tx)` is provided
to return this.
## Signing
### Signing
The standard bytes for signers to sign over is provided by:
@ -151,94 +173,12 @@ TODO
## 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.
TODO
## App3
Putting it all together, we get:
```go
type AnteHandler func(ctx Context, tx Tx) (newCtx Context, result Result, abort bool)
TODO
```
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()`.
# 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)
```