Merge branch 'aditya/guide' into develop

This commit is contained in:
Ethan Buchman 2018-06-16 17:46:59 -07:00
commit c4a70f683c
2 changed files with 169 additions and 21 deletions

View File

@ -99,18 +99,20 @@ type Tx interface {
GetMsg() Msg GetMsg() Msg
// Signatures returns the signature of signers who signed the Msg.
// CONTRACT: Length returned is same as length of
// pubkeys returned from MsgKeySigners, and the order
// matches.
// CONTRACT: If the signature is missing (ie the Msg is
// invalid), then the corresponding signature is
// .Empty().
GetSignatures() []StdSignature
} }
``` ```
The `tx.GetSignatures()` method returns a list of signatures, which must match The standard way to create a transaction from a message is to use the `StdTx` struct defined in the `x/auth` module.
```go
type StdTx struct {
Msg sdk.Msg `json:"msg"`
Fee StdFee `json:"fee"`
Signatures []StdSignature `json:"signatures"`
}
```
The `StdTx.GetSignatures()` method returns a list of signatures, which must match
the list of addresses returned by `tx.Msg.GetSigners()`. The signatures come in the list of addresses returned by `tx.Msg.GetSigners()`. The signatures come in
a standard form: a standard form:
@ -118,6 +120,7 @@ a standard form:
type StdSignature struct { type StdSignature struct {
crypto.PubKey // optional crypto.PubKey // optional
crypto.Signature crypto.Signature
AccountNumber int64
Sequence int64 Sequence int64
} }
``` ```
@ -138,12 +141,21 @@ The address responsible for paying the transactions fee is the first address
returned by msg.GetSigners(). The convenience function `FeePayer(tx Tx)` is provided returned by msg.GetSigners(). The convenience function `FeePayer(tx Tx)` is provided
to return this. to return this.
The standard way to create a transaction from a message is to use the `StdTx`: The standard bytes for signers to sign over is provided by:
```go ```go
type StdTx struct { func StdSignByes(chainID string, accnums []int64, sequences []int64, fee StdFee, msg sdk.Msg) []byte
Msg ```
Signatures []StdSignature
in `x/auth`. The standard way to construct fees to pay for the processing of transactions is:
```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"`
} }
``` ```
@ -154,7 +166,7 @@ specify their own encoding schemes. This enables the SDK to be used as the
framwork for constructing already specified cryptocurrency state machines, for framwork for constructing already specified cryptocurrency state machines, for
instance Ethereum. instance Ethereum.
When initializing an application, a developer must specify a `TxDecoder` When initializing an application, a developer can specify a `TxDecoder`
function which determines how an arbitrary byte array should be unmarshalled function which determines how an arbitrary byte array should be unmarshalled
into a `Tx`: into a `Tx`:
@ -162,8 +174,10 @@ into a `Tx`:
type TxDecoder func(txBytes []byte) (Tx, error) type TxDecoder func(txBytes []byte) (Tx, error)
``` ```
In `Basecoin`, we use the Tendermint wire format and the `go-amino` library for The default tx decoder is the Tendermint wire format which uses the go-amino library
encoding and decoding all message types. The `go-amino` library has the nice for encoding and decoding all message types.
In `Basecoin`, we use the default transaction decoder. The `go-amino` library has the nice
property that it can unmarshal into interface types, but it requires the property that it can unmarshal into interface types, but it requires the
relevant types to be registered ahead of type. Registration happens on a relevant types to be registered ahead of type. Registration happens on a
`Codec` object, so as not to taint the global name space. `Codec` object, so as not to taint the global name space.
@ -182,6 +196,14 @@ unique "prefix bytes" during encoding. A registered type will always use the
same prefix-bytes, regardless of what interface it is satisfying. For more same prefix-bytes, regardless of what interface it is satisfying. For more
details, see the [go-amino documentation](https://github.com/tendermint/go-amino/blob/develop). details, see the [go-amino documentation](https://github.com/tendermint/go-amino/blob/develop).
If you wish to use a custom encoding scheme, you must define a TxDecoder function
and set it as the decoder in your extended baseapp using the `SetTxDecoder(decoder sdk.TxDecoder)`.
Ex:
```go
app.SetTxDecoder(CustomTxDecodeFn)
```
## Storage ## Storage
@ -252,14 +274,14 @@ Many methods on SDK objects receive a context as the first argument.
## Handler ## Handler
Transaction processing in the SDK is defined through `Handler` functions: Message processing in the SDK is defined through `Handler` functions:
```go ```go
type Handler func(ctx Context, tx Tx) Result type Handler func(ctx Context, msg Msg) Result
``` ```
A handler takes a context and a transaction and returns a result. All A handler takes a context and a message and returns a result. All
information necessary for processing a transaction should be available in the information necessary for processing a message should be available in the
context. context.
While the context holds the entire application state (all referenced from the While the context holds the entire application state (all referenced from the
@ -291,15 +313,138 @@ func NewHandler(am sdk.AccountMapper) sdk.Handler {
## AnteHandler ## 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 Fee payment
### Handling Authentication ### 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 and x/auth ## Accounts and x/auth
### sdk.Account ### auth.Account
```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
}
```
Accounts are the standard way for an application to keep track of addresses and their associated balances.
### auth.BaseAccount ### auth.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"`
}
```
The `auth.BaseAccount` struct provides a standard implementation of the Account interface with replay protection.
BaseAccount can be extended by embedding it in your own Account struct.
### auth.AccountMapper ### 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)
```
## Wire codec ## Wire codec
### Why another codec? ### Why another codec?

View File

@ -105,11 +105,14 @@ type KVStore interface {
// Iterator over a domain of keys in ascending order. End is exclusive. // Iterator over a domain of keys in ascending order. End is exclusive.
// Start must be less than end, or the Iterator is invalid. // Start must be less than end, or the Iterator is invalid.
// Iterator must be closed by caller.
// To iterate over entire domain, use store.Iterator(nil, nil)
// CONTRACT: No writes may happen within a domain while an iterator exists over it. // CONTRACT: No writes may happen within a domain while an iterator exists over it.
Iterator(start, end []byte) Iterator Iterator(start, end []byte) Iterator
// Iterator over a domain of keys in descending order. End is exclusive. // Iterator over a domain of keys in descending order. End is exclusive.
// Start must be greater than end, or the Iterator is invalid. // Start must be greater than end, or the Iterator is invalid.
// Iterator must be closed by caller.
// CONTRACT: No writes may happen within a domain while an iterator exists over it. // CONTRACT: No writes may happen within a domain while an iterator exists over it.
ReverseIterator(start, end []byte) Iterator ReverseIterator(start, end []byte) Iterator