From 02b4fd5f1768cce1c4d527a9a1ee2373576c1f71 Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Sat, 1 Apr 2017 15:49:56 -0400 Subject: [PATCH] doc update --- cmd/commands/key.go | 1 + cmd/commands/tx.go | 10 +- docs/guide/example-plugin.md | 194 ++++++++++++++++------------------- docs/guide/plugin-design.md | 4 +- 4 files changed, 99 insertions(+), 110 deletions(-) diff --git a/cmd/commands/key.go b/cmd/commands/key.go index a79ca92f1..a1cec50d1 100644 --- a/cmd/commands/key.go +++ b/cmd/commands/key.go @@ -31,6 +31,7 @@ var ( func newKeyCmd(cmd *cobra.Command, args []string) { key := genKey() keyJSON, err := json.MarshalIndent(key, "", "\t") + fmt.Println(&key) if err != nil { cmn.Exit(fmt.Sprintf("%+v\n", err)) } diff --git a/cmd/commands/tx.go b/cmd/commands/tx.go index 95358221f..e7381b087 100644 --- a/cmd/commands/tx.go +++ b/cmd/commands/tx.go @@ -36,18 +36,20 @@ var ( } ) -//flags var ( + //persistent flags txNodeFlag string - toFlag string amountFlag string fromFlag string seqFlag int gasFlag int feeFlag string - dataFlag string - nameFlag string chainIDFlag string + + //non-persistent flags + toFlag string + dataFlag string + nameFlag string ) func init() { diff --git a/docs/guide/example-plugin.md b/docs/guide/example-plugin.md index 312f3ac23..d9e3a3255 100644 --- a/docs/guide/example-plugin.md +++ b/docs/guide/example-plugin.md @@ -6,10 +6,11 @@ Here, we will demonstrate how to extend the blockchain and CLI to support a simp ## Overview -Creating a new plugin and CLI to support it requires a little bit of boilerplate, but not much. -For convenience, we've implemented an extremely simple example plugin that can be easily modified. -The example is under `docs/guide/src/example-plugin`. -To build your own plugin, copy this folder to a new location and start modifying it there. +Creating a new plugin and CLI to support it requires a little bit of +boilerplate, but not much. For convenience, we've implemented an extremely +simple example plugin that can be easily modified. The example is under +`docs/guide/src/example-plugin`. To build your own plugin, copy this folder to +a new location and start modifying it there. Let's take a look at the files in `docs/guide/src/example-plugin`: @@ -52,16 +53,14 @@ func main() { } ``` -It creates the CLI, exactly like the `basecoin` one. -However, if we want our plugin to be active, -we need to make sure it is registered with the application. -In addition, if we want to send transactions to our plugin, -we need to add a new command to the CLI. -This is where the `cmd.go` comes in. +It creates the CLI, exactly like the `basecoin` one. However, if we want our +plugin to be active, we need to make sure it is registered with the +application. In addition, if we want to send transactions to our plugin, we +need to add a new command to the CLI. This is where the `cmd.go` comes in. ### cmd.go -First we define the new command and associated flag variables +First we define the new CLI command and associated flag variables. ```golang var ( @@ -77,7 +76,10 @@ var ( ) ``` -Next we register the plugin: +Next within the `init` function we register our plugin's flags and register our +custom plugin command with the root command. This creates a new subcommand +under `tx` (defined below), and ensures the plugin is activated when we start +the app. ```golang func init() { @@ -93,59 +95,38 @@ func init() { } ``` -This creates a new subcommand under `tx` (defined below), -and ensures the plugin is activated when we start the app. +We now define the actual function which is called by our CLI command. + +```golang +func examplePluginTxCmd(cmd *cobra.Command, args []string) { + exampleTx := ExamplePluginTx{validFlag} + exampleTxBytes := wire.BinaryBytes(exampleTx) + commands.AppTx("example-plugin", exampleTxBytes) +} +``` + +Our function is a simple command with one boolean flag. However, it actually +inherits the persistent flags from the Basecoin framework. These persistent +flags use pointers to these variables stored in `cmd/commands/tx.go`: ```golang var ( - ExampleFlag = cli.BoolFlag{ - Name: "valid", - Usage: "Set this to make the transaction valid", - } - - ExamplePluginTxCmd = cli.Command{ - Name: "example", - Usage: "Create, sign, and broadcast a transaction to the example plugin", - Action: func(c *cli.Context) error { - return cmdExamplePluginTx(c) - }, - Flags: append(commands.TxFlags, ExampleFlag), - } + //persistent flags + txNodeFlag string + amountFlag string + fromFlag string + seqFlag int + gasFlag int + feeFlag string + chainIDFlag string + + //non-persistent flags + toFlag string + dataFlag string + nameFlag string ) - -func cmdExamplePluginTx(c *cli.Context) error { - exampleFlag := c.Bool("valid") - exampleTx := ExamplePluginTx{exampleFlag} - return commands.AppTx(c, "example-plugin", wire.BinaryBytes(exampleTx)) -} ``` -It's a simple command with one flag, which is just a boolean. -However, it actually inherits more flags from the Basecoin framework: - -```golang -Flags: append(commands.TxFlags, ExampleFlag), -``` - -The `commands.TxFlags` is defined in `cmd/commands/tx.go`: - -```golang -var TxFlags = []cli.Flag{ - NodeFlag, - ChainIDFlag, - - FromFlag, - - AmountFlag, - CoinFlag, - GasFlag, - FeeFlag, - SeqFlag, -} -``` - -It adds all the default flags for a Basecoin transaction. - If we now compile and run our program, we can see all the options: ``` @@ -157,48 +138,51 @@ example-plugin tx example --help The output: ``` -NAME: - example-plugin tx example - Create, sign, and broadcast a transaction to the example plugin +Create, sign, and broadcast a transaction to the example plugin -USAGE: - example-plugin tx example [command options] [arguments...] +Usage: + example-plugin tx example [flags] -OPTIONS: - --node value Tendermint RPC address (default: "tcp://localhost:46657") - --chain_id value ID of the chain for replay protection (default: "test_chain_id") - --from value Path to a private key to sign the transaction (default: "key.json") - --amount value Coins to send in transaction of the format ,,... (eg: 1btc,2gold,5silver) - --gas value The amount of gas for the transaction (default: 0) - --fee value Coins for the transaction fee of the format - --sequence value Sequence number for the account (default: 0) - --valid Set this to make the transaction valid +Flags: + --valid Set this to make transaction valid + +Global Flags: + --amount string Coins to send in transaction of the format ,,... (eg: 1btc,2gold,5silver}, + --chain_id string ID of the chain for replay protection (default "test_chain_id") + --fee string Coins for the transaction fee of the format + --from string Path to a private key to sign the transaction (default "key.json") + --gas int The amount of gas for the transaction + --node string Tendermint RPC address (default "tcp://localhost:46657") + --sequence int Sequence number for the account (-1 to autocalculate}, (default -1) ``` Cool, eh? -Before we move on to `plugin.go`, let's look at the `cmdExamplePluginTx` function in `cmd.go`: +Before we move on to `plugin.go`, let's look at the `examplePluginTxCmd` +function in `cmd.go`: ```golang -func cmdExamplePluginTx(c *cli.Context) error { - exampleFlag := c.Bool("valid") - exampleTx := ExamplePluginTx{exampleFlag} - return commands.AppTx(c, "example-plugin", wire.BinaryBytes(exampleTx)) +func examplePluginTxCmd(cmd *cobra.Command, args []string) { + exampleTx := ExamplePluginTx{validFlag} + exampleTxBytes := wire.BinaryBytes(exampleTx) + commands.AppTx("example-plugin", exampleTxBytes) } ``` We read the flag from the CLI library, and then create the example transaction. -Remember that Basecoin itself only knows about two transaction types, `SendTx` and `AppTx`. -All plugin data must be serialized (ie. encoded as a byte-array) -and sent as data in an `AppTx`. The `commands.AppTx` function does this for us - -it creates an `AppTx` with the corresponding data, signs it, and sends it on to the blockchain. +Remember that Basecoin itself only knows about two transaction types, `SendTx` +and `AppTx`. All plugin data must be serialized (ie. encoded as a byte-array) +and sent as data in an `AppTx`. The `commands.AppTx` function does this for us +- it creates an `AppTx` with the corresponding data, signs it, and sends it on +to the blockchain. ### plugin.go -Ok, now we're ready to actually look at the implementation of the plugin in `plugin.go`. -Note I'll leave out some of the methods as they don't serve any purpose for this example, -but are necessary boilerplate. -Your plugin may have additional requirements that utilize these other methods. -Here's what's relevant for us: +Ok, now we're ready to actually look at the implementation of the plugin in +`plugin.go`. Note I'll leave out some of the methods as they don't serve any +purpose for this example, but are necessary boilerplate. Your plugin may have +additional requirements that utilize these other methods. Here's what's +relevant for us: ```golang type ExamplePluginState struct { @@ -232,15 +216,17 @@ func (ep *ExamplePlugin) SetOption(store types.KVStore, key string, value string } func (ep *ExamplePlugin) RunTx(store types.KVStore, ctx types.CallContext, txBytes []byte) (res abci.Result) { - - // Decode tx + + // Decode txBytes using go-wire. Attempt to write the txBytes to the variable + // tx, if the txBytes have not been properly encoded from a ExamplePluginTx + // struct wire will produce an error. var tx ExamplePluginTx err := wire.ReadBinaryBytes(txBytes, &tx) if err != nil { return abci.ErrBaseEncodingError.AppendLog("Error decoding tx: " + err.Error()) } - // Validate tx + // Perform Transaction Validation if !tx.Valid { return abci.ErrInternalError.AppendLog("Valid must be true") } @@ -248,8 +234,10 @@ func (ep *ExamplePlugin) RunTx(store types.KVStore, ctx types.CallContext, txByt // Load PluginState var pluginState ExamplePluginState stateBytes := store.Get(ep.StateKey()) + // If the state does not exist, stateBytes will be initialized + // as an empty byte array with length of zero if len(stateBytes) > 0 { - err = wire.ReadBinaryBytes(stateBytes, &pluginState) + err = wire.ReadBinaryBytes(stateBytes, &pluginState) //decode using go-wire if err != nil { return abci.ErrInternalError.AppendLog("Error decoding state: " + err.Error()) } @@ -266,12 +254,11 @@ func (ep *ExamplePlugin) RunTx(store types.KVStore, ctx types.CallContext, txByt ``` All we're doing here is defining a state and transaction type for our plugin, -and then using the `RunTx` method to define how the transaction updates the state. -Let's break down `RunTx` in parts. First, we deserialize the transaction: - +and then using the `RunTx` method to define how the transaction updates the +state. Let's break down `RunTx` in parts. First, we deserialize the +transaction: ```golang -// Decode tx var tx ExamplePluginTx err := wire.ReadBinaryBytes(txBytes, &tx) if err != nil { @@ -279,27 +266,26 @@ if err != nil { } ``` -The transaction is expected to be serialized according to Tendermint's "wire" format, -as defined in the `github.com/tendermint/go-wire` package. -If it's not encoded properly, we return an error. +The transaction is expected to be serialized according to Tendermint's "wire" +format, as defined in the `github.com/tendermint/go-wire` package. If it's not +encoded properly, we return an error. If the transaction deserializes correctly, we can now check if it's valid: ```golang -// Validate tx if !tx.Valid { return abci.ErrInternalError.AppendLog("Valid must be true") } ``` -The transaction is valid if the `Valid` field is set, otherwise it's not - simple as that. -Finally, we can update the state. In this example, the state simply counts how many valid transactions -we've processed. But the state itself is serialized and kept in some `store`, which is typically a Merkle tree. -So first we have to load the state from the store and deserialize it: +The transaction is valid if the `Valid` field is set, otherwise it's not - +simple as that. Finally, we can update the state. In this example, the state +simply counts how many valid transactions we've processed. But the state itself +is serialized and kept in some `store`, which is typically a Merkle tree. So +first we have to load the state from the store and deserialize it: ```golang -// Load PluginState var pluginState ExamplePluginState stateBytes := store.Get(ep.StateKey()) if len(stateBytes) > 0 { @@ -402,5 +388,5 @@ basecoin CLI to activate the plugin on the blockchain and to send transactions t Hopefully by now you have some ideas for your own plugin, and feel comfortable implementing them. In the [next tutorial](more-examples.md), we tour through some other plugin examples, -adding features for minting new coins, voting, and changing the Tendermint validator set. +addin mple-plugin query ExamplePlugin.Statefeatures for minting new coins, voting, and changing the Tendermint validator set. But first, you may want to learn a bit more about [the design of the plugin system](plugin-design.md) diff --git a/docs/guide/plugin-design.md b/docs/guide/plugin-design.md index f59ce736c..e8e7dc92f 100644 --- a/docs/guide/plugin-design.md +++ b/docs/guide/plugin-design.md @@ -43,8 +43,8 @@ type Plugin interface { // Other ABCI message handlers SetOption(store KVStore, key string, value string) (log string) InitChain(store KVStore, vals []*abci.Validator) - BeginBlock(store KVStore, height uint64) - EndBlock(store KVStore, height uint64) []*abci.Validator + BeginBlock(store KVStore, hash []byte, header *abci.Header) + EndBlock(store KVStore, height uint64) (res abci.ResponseEndBlock) } type CallContext struct {