docs: more on txs, context, handlers

This commit is contained in:
Ethan Buchman 2018-01-18 17:31:35 -05:00 committed by Jae Kwon
parent 94999ad455
commit b4e4881261
1 changed files with 97 additions and 1 deletions

View File

@ -64,9 +64,40 @@ to enforce that message contents are well formed before any actual logic begins.
Finally, messages can provide generic access to their contents via `Get(key)`, Finally, messages can provide generic access to their contents via `Get(key)`,
but this is mostly for convenience and not type-safe. but this is mostly for convenience and not type-safe.
For instance, the `Basecoin` message types are defined in `x/bank/tx.go`:
```
type SendMsg struct {
Inputs []Input `json:"inputs"`
Outputs []Output `json:"outputs"`
}
type IssueMsg struct {
Banker crypto.Address `json:"banker"`
Outputs []Output `json:"outputs"`
}
```
Each specifies the addresses that must sign the message:
```
func (msg SendMsg) GetSigners() []crypto.Address {
addrs := make([]crypto.Address, len(msg.Inputs))
for i, in := range msg.Inputs {
addrs[i] = in.Address
}
return addrs
}
func (msg IssueMsg) GetSigners() []crypto.Address {
return []crypto.Address{msg.Banker}
}
```
### Transactions ### Transactions
For a message to actually be valid, it must be wrapped as a `Tx`, which includes information for authentication: A transaction is a message with additional information for authentication:
``` ```
type Tx interface { type Tx interface {
@ -114,10 +145,75 @@ is forever stored by the application and can be left out of transactions.
Transactions can also specify the address responsible for paying the transaction's fees using the `tx.GetFeePayer()` method. Transactions can also specify the address responsible for paying the transaction's fees using the `tx.GetFeePayer()` method.
The standard way to create a transaction from a message is to use the `StdTx`:
```
type StdTx struct {
Msg
Signatures []StdSignature
}
```
### Encoding and Decoding Transactions
Messages and transactions are designed to be generic enough for developers to specify their own encoding schemes.
This enables the SDK to be used as the framwork for constructing already specified cryptocurrency state machines,
for instance Ethereum.
When initializing an application, a developer must specify a `TxDecoder` function which determines how an arbitrary
byte array should be unmarshalled into a `Tx`:
```
type TxDecoder func(txBytes []byte) (Tx, error)
```
In `Basecoin`, we use the Tendermint wire format and the `go-wire` library for encoding and decoding all message types.
The `go-wire` library has the nice property that it can unmarshal into interface types, but it requires the relevant types
to be registered ahead of type. Registration happens on a `Codec` object, so as not to taint the global name space.
For instance, in `Basecoin`, we wish to register the `SendMsg` and `IssueMsg` types:
```
cdc.RegisterInterface((*sdk.Msg)(nil), nil)
cdc.RegisterConcrete(bank.SendMsg{}, "cosmos-sdk/SendMsg", nil)
cdc.RegisterConcrete(bank.IssueMsg{}, "cosmos-sdk/IssueMsg", nil)
```
Note how each concrete type is given a name - these name determines the types unique "prefix bytes" during encoding.
A registered type will always use the same prefix-bytes, regardless of what interface it is satisfying.
For more details, see the [go-wire documentation]().
## Context ## Context
The SDK uses a `Context` to propogate common information across functions. The `Context` is modelled
off of the Golang `context.Context` object, which has become ubiquitous in networking middleware
and routing applications as a means to easily propogate request context through handler functions.
The main information stored in the `Context` includes the application MultiStore (see below),
the last block header, and the transaction bytes. Effectively, the context contains all data that
may be necessary for processing a transaction.
Many methods on SDK objects receive a context as the first argument.
## Handlers ## Handlers
Transaction processing in the SDK is defined through `Handler` functions:
```
type Handler func(ctx Context, tx Tx) Result
```
A handler takes a context and a transaction and returns a result. All information necessary
for processing a transaction should be available in the context.
While the context holds the entire application store, a particular handler may only need
some subset of the store. Access to substores is managed using capabilities -
when a handler is initialized, it is passed capability keys that determine which parts of the
store it can access.
TODO: example
## Store ## Store
## App ## App