fixes from review

This commit is contained in:
Ethan Buchman 2018-06-28 19:06:10 -04:00
parent 3a96f8ffd1
commit e8946e9b36
8 changed files with 62 additions and 35 deletions

View File

@ -18,15 +18,15 @@ type Msg interface {
// Must be alphanumeric or empty.
// Must correspond to name of message handler (XXX).
Type() string
// ValidateBasic does a simple validation check that
// doesn't require access to any other information.
ValidateBasic() error
// Get the canonical byte representation of the Msg.
// This is what is signed.
GetSignBytes() []byte
// ValidateBasic does a simple validation check that
// doesn't require access to any other information.
ValidateBasic() error
// Signers returns the addrs of signers that must sign.
// CONTRACT: All signatures must be present to be valid.
// CONTRACT: Returns addrs in some deterministic order.
@ -38,10 +38,6 @@ type Msg interface {
The `Msg` interface allows messages to define basic validity checks, as well as
what needs to be signed and who needs to sign it.
Addresses in the SDK are arbitrary byte arrays that are hex-encoded when
displayed as a string or rendered in JSON. Typically, addresses are the hash of
a public key.
For instance, take the simple token sending message type from app1.go:
```go
@ -74,8 +70,14 @@ func (msg MsgSend) GetSigners() []sdk.Address {
}
```
Note Addresses in the SDK are arbitrary byte arrays that are [Bech32](TODO) encoded
when displayed as a string or rendered in JSON. Typically, addresses are the hash of
a public key, so we can use them to uniquely identify the required signers for a
transaction.
The basic validity check ensures the From and To address are specified and the
amount is positive:
Amount is positive:
```go
// Implements Msg. Ensure the addresses are good and the
@ -94,6 +96,8 @@ func (msg MsgSend) ValidateBasic() sdk.Error {
}
```
Note the `ValidateBasic` method is called automatically by the SDK!
## KVStore
The basic persistence layer for an SDK application is the KVStore:
@ -240,8 +244,10 @@ func NewApp1Handler(keyAcc *sdk.KVStoreKey) sdk.Handler {
We have only a single message type, so just one message-specific function to define, `handleMsgSend`.
Note this handler has unfettered access to the store specified by the capability key `keyAcc`. So it must also define items in the store are encoded.
For this first example, we will define a simple account that is JSON encoded:
Note this handler has unrestricted access to the store specified by the capability key `keyAcc`,
so it must define what to store and how to encode it. Later, we'll introduce
higher-level abstractions so Handlers are restricted in what they can do.
For this first example, we use a simple account that is JSON encoded:
```go
type appAccount struct {
@ -249,7 +255,8 @@ type appAccount struct {
}
```
Coins is a useful type provided by the SDK for multi-asset accounts. While we could just use an integer here for a single coin type,
Coins is a useful type provided by the SDK for multi-asset accounts.
We could just use an integer here for a single coin type, but
it's worth [getting to know Coins](TODO).
@ -258,6 +265,7 @@ Now we're ready to handle the MsgSend:
```go
// Handle MsgSend.
// NOTE: msg.From, msg.To, and msg.Amount were already validated
// in ValidateBasic().
func handleMsgSend(ctx sdk.Context, key *sdk.KVStoreKey, msg MsgSend) sdk.Result {
// Load the store.
store := ctx.KVStore(key)
@ -359,7 +367,7 @@ func handleTo(store sdk.KVStore, to sdk.Address, amt sdk.Coins) sdk.Result {
And that's that!
# Tx
## Tx
The final piece before putting it all together is the `Tx`.
While `Msg` contains the content for particular functionality in the application, the actual input
@ -408,7 +416,7 @@ func txDecoder(txBytes []byte) (sdk.Tx, sdk.Error) {
}
```
# BaseApp
## BaseApp
Finally, we stitch it all together using the `BaseApp`.
@ -467,6 +475,25 @@ After setting the transaction decoder and the message handling routes, the final
step is to mount the stores and load the latest version.
Since we only have one store, we only mount one.
## Execution
We're now done the core logic of the app! From here, we could write transactions
in Go and execute them against the application using the `app.DeliverTx` method.
In a real setup, the app would run as an ABCI application and
would be driven by blocks of transactions from the Tendermint consensus engine.
Later in the tutorial, we'll connect our app to a complete suite of components
for running and using a live blockchain application. For complete details on
how ABCI applications work, see the [ABCI documentation](TODO).
For now, we note the follow sequence of events occurs when a transaction is
received (through `app.DeliverTx`):
- serialized transaction is received by `app.DeliverTx`
- transaction is deserialized using `TxDecoder`
- for each message in the transaction, run `msg.ValidateBasic()`
- for each message in the transaction, load the appropriate handler and execute
it with the message
## Conclusion
We now have a complete implementation of a simple app!

View File

@ -84,10 +84,10 @@ Now that we're using Amino, we can embed the `Msg` interface directly in our
```go
// Simple tx to wrap the Msg.
type app2Tx struct {
sdk.Msg
sdk.Msg
PubKey crypto.PubKey
Signature crypto.Signature
Signature crypto.Signature
}
// This tx only has one Msg.

View File

@ -113,9 +113,9 @@ Then we can get, modify, and set accounts. For instance, we could double the
amount of coins in an account:
```go
acc := GetAccount(ctx, addr)
acc := accountMapper.GetAccount(ctx, addr)
acc.SetCoins(acc.Coins.Plus(acc.Coins))
acc.SetAccount(ctx, addr)
accountMapper.SetAccount(ctx, addr)
```
Note that the `AccountMapper` takes a `Context` as the first argument, and will

View File

@ -106,6 +106,7 @@ func (msg MsgSend) Tags() sdk.Tags {
// Handle MsgSend.
// NOTE: msg.From, msg.To, and msg.Amount were already validated
// in ValidateBasic().
func handleMsgSend(key *sdk.KVStoreKey) sdk.Handler {
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
sendMsg, ok := msg.(MsgSend)
@ -115,7 +116,6 @@ func handleMsgSend(key *sdk.KVStoreKey) sdk.Handler {
return sdk.NewError(2, 1, "Send Message is malformed").Result()
}
// Load the store.
store := ctx.KVStore(key)

View File

@ -1,9 +1,9 @@
package app
import (
"bytes"
"encoding/json"
"fmt"
"reflect"
"github.com/tendermint/go-crypto"
cmn "github.com/tendermint/tmlibs/common"
@ -76,9 +76,9 @@ type CoinMetadata struct {
// if he is the issuer in Coin Metadata
// Implements sdk.Msg Interface
type MsgIssue struct {
Issuer sdk.Address
Issuer sdk.Address
Receiver sdk.Address
Coin sdk.Coin
Coin sdk.Coin
}
// Implements Msg.
@ -179,7 +179,7 @@ func handleMetaData(store sdk.KVStore, issuer sdk.Address, coin sdk.Coin) sdk.Re
}
// Msg Issuer is not authorized to issue these coins
if !reflect.DeepEqual(metadata.Issuer, issuer) {
if !bytes.Equal(metadata.Issuer, issuer) {
return sdk.ErrUnauthorized(fmt.Sprintf("Msg Issuer cannot issue tokens: %s", coin.Denom)).Result()
}
@ -198,11 +198,10 @@ func handleMetaData(store sdk.KVStore, issuer sdk.Address, coin sdk.Coin) sdk.Re
// Update store with new metadata
store.Set([]byte(coin.Denom), val)
return sdk.Result{}
}
//------------------------------------------------------------------
// Tx
@ -246,7 +245,7 @@ func antehandler(ctx sdk.Context, tx sdk.Tx) (_ sdk.Context, _ sdk.Result, abort
sig := appTx.GetSignatures()[i]
// check that submitted pubkey belongs to required address
if !reflect.DeepEqual(sig.PubKey.Address(), addr) {
if !bytes.Equal(sig.PubKey.Address(), addr) {
return ctx, sdk.ErrUnauthorized("Provided Pubkey does not match required address").Result(), true
}

View File

@ -1,8 +1,8 @@
package app
import (
"bytes"
"encoding/json"
"reflect"
"fmt"
cmn "github.com/tendermint/tmlibs/common"
@ -94,13 +94,13 @@ func betterHandleMsgIssue(metadataMapper MetaDataMapper, accountKeeper bank.Keep
if res := betterHandleMetaData(ctx, metadataMapper, issueMsg.Issuer, issueMsg.Coin); !res.IsOK() {
return res
}
// Add newly issued coins to output address
_, _, err := accountKeeper.AddCoins(ctx, issueMsg.Receiver, []sdk.Coin{issueMsg.Coin})
if err != nil {
return err.Result()
}
return sdk.Result{
// Return result with Issue msg tags
Tags: issueMsg.Tags(),
@ -117,7 +117,7 @@ func betterHandleMetaData(ctx sdk.Context, metadataMapper MetaDataMapper, issuer
}
// Msg Issuer is not authorized to issue these coins
if !reflect.DeepEqual(metadata.Issuer, issuer) {
if !bytes.Equal(metadata.Issuer, issuer) {
return sdk.ErrUnauthorized(fmt.Sprintf("Msg Issuer cannot issue tokens: %s", coin.Denom)).Result()
}

View File

@ -1,6 +1,7 @@
package app
import (
"bytes"
"encoding/json"
"fmt"
"reflect"
@ -172,7 +173,7 @@ func evenBetterHandleMetaData(ctx sdk.Context, metadataMapper MetaDataMapper, is
}
// Msg Issuer not authorized to issue these coins
if !reflect.DeepEqual(metadata.Issuer, issuer) {
if !bytes.Equal(metadata.Issuer, issuer) {
return sdk.ErrUnauthorized(fmt.Sprintf("Msg Issuer cannot issue tokens: %s", coin.Denom)).Result()
}

View File

@ -11,13 +11,13 @@ type Msg interface {
// Must be alphanumeric or empty.
Type() string
// Get the canonical byte representation of the Msg.
GetSignBytes() []byte
// ValidateBasic does a simple validation check that
// doesn't require access to any other information.
ValidateBasic() Error
// Get the canonical byte representation of the Msg.
GetSignBytes() []byte
// Signers returns the addrs of signers that must sign.
// CONTRACT: All signatures must be present to be valid.
// CONTRACT: Returns addrs in some deterministic order.