diff --git a/docs/basics.md b/docs/basics.md index 8948895453..4599e2bd7f 100644 --- a/docs/basics.md +++ b/docs/basics.md @@ -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)`, 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 -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 { @@ -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. +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 +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 +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 ## App