From e8946e9b36b72c97acd1d393d8f8dc45ce9b869c Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Thu, 28 Jun 2018 19:06:10 -0400 Subject: [PATCH] fixes from review --- docs/core/app1.md | 55 ++++++++++++++++++++++++++++---------- docs/core/app2.md | 6 ++--- docs/core/app3.md | 4 +-- docs/core/examples/app1.go | 2 +- docs/core/examples/app2.go | 13 +++++---- docs/core/examples/app3.go | 8 +++--- docs/core/examples/app4.go | 3 ++- types/tx_msg.go | 6 ++--- 8 files changed, 62 insertions(+), 35 deletions(-) diff --git a/docs/core/app1.md b/docs/core/app1.md index 08fcf73fe3..da51b0a89a 100644 --- a/docs/core/app1.md +++ b/docs/core/app1.md @@ -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! diff --git a/docs/core/app2.md b/docs/core/app2.md index f9a2e060f3..2cf62b8a25 100644 --- a/docs/core/app2.md +++ b/docs/core/app2.md @@ -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. diff --git a/docs/core/app3.md b/docs/core/app3.md index 3f35b9c1eb..141264ac25 100644 --- a/docs/core/app3.md +++ b/docs/core/app3.md @@ -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 diff --git a/docs/core/examples/app1.go b/docs/core/examples/app1.go index 56fcea0ec9..2e57743f77 100644 --- a/docs/core/examples/app1.go +++ b/docs/core/examples/app1.go @@ -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) diff --git a/docs/core/examples/app2.go b/docs/core/examples/app2.go index f0260ac432..bed5b0f447 100644 --- a/docs/core/examples/app2.go +++ b/docs/core/examples/app2.go @@ -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 } diff --git a/docs/core/examples/app3.go b/docs/core/examples/app3.go index e20bbcb70c..a9a4b9759d 100644 --- a/docs/core/examples/app3.go +++ b/docs/core/examples/app3.go @@ -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() } diff --git a/docs/core/examples/app4.go b/docs/core/examples/app4.go index 564039798d..5494e1bd2d 100644 --- a/docs/core/examples/app4.go +++ b/docs/core/examples/app4.go @@ -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() } diff --git a/types/tx_msg.go b/types/tx_msg.go index 12146f5b73..c1af91df82 100644 --- a/types/tx_msg.go +++ b/types/tx_msg.go @@ -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.