Review feedback from Adrian

This commit is contained in:
Jae Kwon 2018-01-28 15:17:59 -08:00
parent c368b1062c
commit 3b09a3033a
2 changed files with 89 additions and 78 deletions

View File

@ -47,28 +47,26 @@ type Msg interface {
```
Messages must specify their type via the `Type()` method. The
type should correspond to the messages handler, so there can be
many messages with the same type.
Messages must specify their type via the `Type()` method. The type should
correspond to the messages handler, so there can be many messages with the same
type.
Messages must also specify how they are to be authenticated. The
`GetSigners()` method return a list of addresses that must sign
the message, while the `GetSignBytes()` method returns the bytes
that must be signed for a signature to be valid.
Messages must also specify how they are to be authenticated. The `GetSigners()`
method return a list of addresses that must sign the message, while the
`GetSignBytes()` method returns the bytes that must be signed for a signature
to be valid.
Addresses in the SDK are arbitrary byte arrays that are
hex-encoded when displayed as a string or rendered in JSON.
Addresses in the SDK are arbitrary byte arrays that are hex-encoded when
displayed as a string or rendered in JSON.
Messages can specify basic self-consistency checks using the
`ValidateBasic()` method to enforce that message contents are
well formed before any actual logic begins.
Messages can specify basic self-consistency checks using the `ValidateBasic()`
method 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.
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`:
For instance, the `Basecoin` message types are defined in `x/bank/tx.go`:
```golang
type SendMsg struct {
@ -100,8 +98,7 @@ func (msg IssueMsg) GetSigners() []crypto.Address {
### Transactions
A transaction is a message with additional information for
authentication:
A transaction is a message with additional information for authentication:
```golang
type Tx interface {
@ -127,9 +124,9 @@ type Tx interface {
}
```
The `tx.GetSignatures()` method returns a list of signatures,
which must match the list of addresses returned by
`tx.Msg.GetSigners()`. The signatures come in a standard form:
The `tx.GetSignatures()` method returns a list of signatures, which must match
the list of addresses returned by `tx.Msg.GetSigners()`. The signatures come in
a standard form:
```golang
type StdSignature struct {
@ -139,25 +136,22 @@ type StdSignature struct {
}
```
It contains the signature itself, as well as the corresponding
account's sequence number. The sequence number is expected to
increment every time a message is signed by a given account.
This prevents "replay attacks", where the same message could be
executed over and over again.
It contains the signature itself, as well as the corresponding account's
sequence number. The sequence number is expected to increment every time a
message is signed by a given account. This prevents "replay attacks", where
the same message could be executed over and over again.
The `StdSignature` can also optionally include the public key for
verifying the signature. An application can store the public key
for each address it knows about, making it optional to include
the public key in the transaction. In the case of Basecoin, the
public key only needs to be included in the first transaction
send by a given account - after that, the public key is forever
The `StdSignature` can also optionally include the public key for verifying the
signature. An application can store the public key for each address it knows
about, making it optional to include the public key in the transaction. In the
case of Basecoin, the public key only needs to be included in the first
transaction send by a given account - after that, the public key 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`:
The standard way to create a transaction from a message is to use the `StdTx`:
```golang
type StdTx struct {
@ -168,28 +162,27 @@ type StdTx struct {
### 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.
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`:
When initializing an application, a developer must specify a `TxDecoder`
function which determines how an arbitrary byte array should be unmarshalled
into a `Tx`:
```golang
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.
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:
For instance, in `Basecoin`, we wish to register the `SendMsg` and `IssueMsg`
types:
```golang
cdc.RegisterInterface((*sdk.Msg)(nil), nil)
@ -197,11 +190,10 @@ 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]().
Note how each concrete type is given a name - these name determine the type's
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](https://github.com/tendermint/go-wire/blob/develop).
## MultiStore
@ -222,38 +214,56 @@ TODO:
## 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 SDK uses a `Context` to propogate common information across functions. The
`Context` is modeled after 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.
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.
Many methods on SDK objects receive a context as the first argument.
## Handler
Transaction processing in the SDK is defined through `Handler`
functions:
Transaction processing in the SDK is defined through `Handler` functions:
```golang
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.
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.
While the context holds the entire application state (all referenced from the
root MultiStore), a particular handler only needs a particular kind of access
to a particular store (or two or more). Access to stores is managed using
capabilities keys and mappers. When a handler is initialized, it is passed a
key or mapper that gives it access to the relevant stores.
```golang
// File: cosmos-sdk/examples/basecoin/app/init_stores.go
app.BaseApp.MountStore(app.capKeyMainStore, sdk.StoreTypeIAVL)
app.accountMapper = auth.NewAccountMapper(
app.capKeyMainStore, // target store
&types.AppAccount{}, // prototype
)
// File: cosmos-sdk/examples/basecoin/app/init_handlers.go
app.router.AddRoute("bank", bank.NewHandler(app.accountMapper))
// File: cosmos-sdk/x/bank/handler.go
// NOTE: Technically, NewHandler only needs a CoinMapper
func NewHandler(am sdk.AccountMapper) sdk.Handler {
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
cm := CoinMapper{am}
...
}
}
```
## AnteHandler

View File

@ -7,6 +7,7 @@ import (
)
// Handle all "bank" type messages.
// NOTE: Technically, NewHandler only needs a CoinMapper
func NewHandler(am sdk.AccountMapper) sdk.Handler {
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {