move AnteHandler to app2 and some cleanup in app3
This commit is contained in:
parent
41c61d2cc7
commit
354f9760f8
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
```
|
||||
|
||||
|
|
Loading…
Reference in New Issue