diff --git a/Makefile b/Makefile index 159618cc9eb2..c7574c286578 100644 --- a/Makefile +++ b/Makefile @@ -9,6 +9,7 @@ build: install: go install ./cmd/... + go install ./docs/guide/counter/cmd/... dist: @bash scripts/dist.sh diff --git a/circle.yml b/circle.yml index ea4110e9da56..3034760d6224 100644 --- a/circle.yml +++ b/circle.yml @@ -19,8 +19,5 @@ dependencies: test: override: - - "cd $REPO && glide install && go install ./cmd/..." + - "cd $REPO && make all" - ls $GOPATH/bin - - "cd $REPO && make test" - - diff --git a/cmd/basecli/commands/apptx.go b/cmd/basecli/commands/apptx.go index 1e68600e1708..1325427d37f9 100644 --- a/cmd/basecli/commands/apptx.go +++ b/cmd/basecli/commands/apptx.go @@ -79,7 +79,7 @@ func (a *AppTx) AddSigner(pk crypto.PubKey) { // but that code is too ugly now, needs refactor.. func (a *AppTx) ValidateBasic() error { if a.chainID == "" { - return errors.New("No chainId specified") + return errors.New("No chain-id specified") } in := a.Tx.Input if len(in.Address) != 20 { diff --git a/cmd/basecli/commands/sendtx.go b/cmd/basecli/commands/sendtx.go index 178e4e37d928..15c5d5871950 100644 --- a/cmd/basecli/commands/sendtx.go +++ b/cmd/basecli/commands/sendtx.go @@ -80,7 +80,7 @@ func (s *SendTx) AddSigner(pk crypto.PubKey) { // but that code is too ugly now, needs refactor.. func (s *SendTx) ValidateBasic() error { if s.chainID == "" { - return errors.New("No chainId specified") + return errors.New("No chain-id specified") } for _, in := range s.Tx.Inputs { if len(in.Address) != 20 { diff --git a/cmd/counter/cmd.go b/cmd/counter/cmd.go deleted file mode 100644 index 63431591bbde..000000000000 --- a/cmd/counter/cmd.go +++ /dev/null @@ -1,11 +0,0 @@ -package main - -import ( - "github.com/tendermint/basecoin/cmd/commands" - "github.com/tendermint/basecoin/plugins/counter" - "github.com/tendermint/basecoin/types" -) - -func init() { - commands.RegisterStartPlugin("counter", func() types.Plugin { return counter.New() }) -} diff --git a/docs/guide/basecoin-plugins.md b/docs/guide/basecoin-plugins.md index 4e1c2251c149..27d65bb851de 100644 --- a/docs/guide/basecoin-plugins.md +++ b/docs/guide/basecoin-plugins.md @@ -1,79 +1,93 @@ # Basecoin Plugins -In the [previous guide](basecoin-basics.md), -we saw how to use the `basecoin` tool to start a blockchain and send transactions. -We also learned about `Account` and `SendTx`, the basic data types giving us a multi-asset cryptocurrency. -Here, we will demonstrate how to extend the `basecoin` tool to use another transaction type, the `AppTx`, -to send data to a custom plugin. In this case we use a simple plugin that takes a single boolean argument, -and only accept the transaction if the argument is set to `true`. +In the [previous guide](basecoin-basics.md), we saw how to use the `basecoin` +tool to start a blockchain and send transactions. We also learned about +`Account` and `SendTx`, the basic data types giving us a multi-asset +cryptocurrency. Here, we will demonstrate how to extend the `basecoin` tool to +use another transaction type, the `AppTx`, to send data to a custom plugin. In +this example we explore a simple plugin name `counter`. ## Example Plugin -The design of the `basecoin` tool makes it easy to extend for custom functionality. -To see what this looks like, install the `example-plugin` tool: +The design of the `basecoin` tool makes it easy to extend for custom +functionality. The Counter plugin is bundled with basecoin, so if you have +already [installed basecoin](install.md) then you should be able to run a full +node with `counter` and the a light-client `countercli` from terminal. The +Counter plugin is just like the `basecoin` tool. They both use the same +library of commands, including one for signing and broadcasting `SendTx`. + +Counter transactions take two custom inputs, a boolean argument named `valid`, +and a coin amount named `countfee`. The transaction is only accepted if both +`valid` is set to true and the transaction input coins is greater than +`countfee` that the user provides. + +A new blockchain can be initialized and started just like with in the [previous +guide](basecoin-basics.md): ``` -cd $GOPATH/src/github.com/tendermint/basecoin -go install ./docs/guide/src/example-plugin -``` +counter init +countercli keys new cool +countercli keys new friend -The `example-plugin` tool is just like the `basecoin` tool. -They both use the same library of commands, including one for signing and broadcasting `SendTx`. -See `example-plugin --help` for details. +GENKEY=`countercli keys get cool -o json | jq .pubkey.data` +GENJSON=`cat ~/.counter/genesis.json` +echo $GENJSON | jq '.app_options.accounts[0].pub_key.data='$GENKEY > ~/.counter/genesis.json -A new blockchain can be initialized and started just like with `basecoin`: +counter start -``` -example-plugin init -example-plugin start ``` -The default files are stored in `~/.basecoin-example-plugin`. -In another window, we can send a `SendTx` like we are used to: +The default files are stored in `~/.counter`. In another window we can +initialize the light-client and send a transaction: ``` -example-plugin tx send --to 0x1DA7C74F9C219229FD54CC9F7386D5A3839F0090 --amount 1mycoin +countercli init --chain-id=test_chain_id --node=tcp://localhost:46657 + +YOU=`countercli keys get friend -o=json | jq .address | tr -d '"'` +countercli tx send --name=cool --amount=1000mycoin --to=0x$YOU --sequence=1 ``` -But the `example-plugin` tool has an additional command, `example-plugin tx example`, -which crafts an `AppTx` specifically for our example plugin. -This command lets you send a single boolean argument: +But the Counter has an additional command, `countercli tx counter`, which +crafts an `AppTx` specifically for this plugin: ``` -example-plugin tx example --amount 1mycoin -example-plugin tx example --amount 1mycoin --valid +countercli tx counter --name cool --amount=1mycoin --sequence=2 +countercli tx counter --name cool --amount=1mycoin --sequence=3 --valid ``` -The first transaction is rejected by the plugin because it was not marked as valid, while the second transaction passes. -We can build plugins that take many arguments of different types, and easily extend the tool to accomodate them. -Of course, we can also expose queries on our plugin: +The first transaction is rejected by the plugin because it was not marked as +valid, while the second transaction passes. We can build plugins that take +many arguments of different types, and easily extend the tool to accomodate +them. Of course, we can also expose queries on our plugin: ``` -example-plugin query ExamplePlugin.State +countercli query counter ``` -Note the `"value":"0101"`. This is the serialized form of the state, -which contains only an integer, the number of valid transactions. -If we send another transaction, and then query again, we will see the value increment: +Tada! We can now see that our custom counter plugin tx went through. You +should see a Counter value of 1 representing the number of valid transactions. +If we send another transaction, and then query again, we will see the value +increment: ``` -example-plugin tx example --valid --amount 1mycoin -example-plugin query ExamplePlugin.State +countercli tx counter --name cool --amount=1mycoin --sequence=4 --valid +countercli query counter ``` -The value should now be `0102`, because we sent a second valid transaction. -Notice how the result of the query comes with a proof. -This is a Merkle proof that the state is what we say it is. -In a latter [guide on InterBlockchain Communication](ibc.md), -we'll put this proof to work! +The value Counter value should be 2, because we sent a second valid transaction. +Notice how the result of the query comes with a proof. This is a Merkle proof +that the state is what we say it is. In a latter [guide on InterBlockchain +Communication](ibc.md), we'll put this proof to work! -Now, before we implement our own plugin and tooling, it helps to understand the `AppTx` and the design of the plugin system. +Now, before we implement our own plugin and tooling, it helps to understand the +`AppTx` and the design of the plugin system. ## AppTx -The `AppTx` is similar to the `SendTx`, but instead of sending coins from inputs to outputs, -it sends coins from one input to a plugin, and can also send some data. +The `AppTx` is similar to the `SendTx`, but instead of sending coins from +inputs to outputs, it sends coins from one input to a plugin, and can also send +some data. ```golang type AppTx struct { @@ -85,13 +99,15 @@ type AppTx struct { } ``` -The `AppTx` enables Basecoin to be extended with arbitrary additional functionality through the use of plugins. -The `Name` field in the `AppTx` refers to the particular plugin which should process the transaction, -and the `Data` field of the `AppTx` is the data to be forwarded to the plugin for processing. +The `AppTx` enables Basecoin to be extended with arbitrary additional +functionality through the use of plugins. The `Name` field in the `AppTx` +refers to the particular plugin which should process the transaction, and the +`Data` field of the `AppTx` is the data to be forwarded to the plugin for +processing. -Note the `AppTx` also has a `Gas` and `Fee`, with the same meaning as for the `SendTx`. -It also includes a single `TxInput`, which specifies the sender of the transaction, -and some coins that can be forwarded to the plugin as well. +Note the `AppTx` also has a `Gas` and `Fee`, with the same meaning as for the +`SendTx`. It also includes a single `TxInput`, which specifies the sender of +the transaction, and some coins that can be forwarded to the plugin as well. ## Plugins @@ -120,43 +136,59 @@ type CallContext struct { } ``` -The workhorse of the plugin is `RunTx`, which is called when an `AppTx` is processed. -The `Data` from the `AppTx` is passed in as the `txBytes`, -while the `Input` from the `AppTx` is used to populate the `CallContext`. +The workhorse of the plugin is `RunTx`, which is called when an `AppTx` is +processed. The `Data` from the `AppTx` is passed in as the `txBytes`, while +the `Input` from the `AppTx` is used to populate the `CallContext`. -Note that `RunTx` also takes a `KVStore` - this is an abstraction for the underlying Merkle tree which stores the account data. -By passing this to the plugin, we enable plugins to update accounts in the Basecoin state directly, -and also to store arbitrary other information in the state. -In this way, the functionality and state of a Basecoin-derived cryptocurrency can be greatly extended. -One could imagine going so far as to implement the Ethereum Virtual Machine as a plugin! +Note that `RunTx` also takes a `KVStore` - this is an abstraction for the +underlying Merkle tree which stores the account data. By passing this to the +plugin, we enable plugins to update accounts in the Basecoin state directly, +and also to store arbitrary other information in the state. In this way, the +functionality and state of a Basecoin-derived cryptocurrency can be greatly +extended. One could imagine going so far as to implement the Ethereum Virtual +Machine as a plugin! -For details on how to initialize the state using `SetOption`, see the [guide to using the basecoin tool](basecoin-tool.md#genesis). +For details on how to initialize the state using `SetOption`, see the [guide to +using the basecoin tool](basecoin-tool.md#genesis). ## Implement your own -To implement your own plugin and tooling, make a copy of `docs/guide/src/example-plugin`, -and modify the code accordingly. Here, we will briefly describe the design and the changes to be made, -but see the code for more details. +To implement your own plugin and tooling, make a copy of +`docs/guide/counter`, and modify the code accordingly. Here, we will +briefly describe the design and the changes to be made, but see the code for +more details. + +First is the `cmd/counter/main.go`, which drives the program. It can be left +alone, but you should change any occurrences of `counter` to whatever your +plugin tool is going to be called. -First is the `main.go`, which drives the program. It can be left alone, but you should change any occurences of `example-plugin` -to whatever your plugin tool is going to be called. +The light-client which is located in `cmd/countercli/main.go` allows for is +where transaction and query commands are designated. Similarity this command +can be mostly left alone besides replacing the application name and adding +references to new plugin commands -Next is the `cmd.go`. This is where we extend the tool with any new commands and flags we need to send transactions to our plugin. -Note the `init()` function, where we register a new transaction subcommand with `RegisterTxSubcommand`, -and where we load the plugin into the Basecoin app with `RegisterStartPlugin`. +Next is the custom commands in `cmd/countercli/commands/`. These files is +where we extend the tool with any new commands and flags we need to send +transactions or queries to our plugin. Note the `init()` function, where we +register a new transaction subcommand with `RegisterTxSubcommand`, and where we +load the plugin into the Basecoin app with `RegisterStartPlugin`. -Finally is the `plugin.go`, where we provide an implementation of the `Plugin` interface. -The most important part of the implementation is the `RunTx` method, which determines the meaning of the data -sent along in the `AppTx`. In our example, we define a new transaction type, the `ExamplePluginTx`, which -we expect to be encoded in the `AppTx.Data`, and thus to be decoded in the `RunTx` method, and used to update the plugin state. +Finally is `plugins/counter/counter.go`, where we provide an implementation of +the `Plugin` interface. The most important part of the implementation is the +`RunTx` method, which determines the meaning of the data sent along in the +`AppTx`. In our example, we define a new transaction type, the `CounterTx`, +which we expect to be encoded in the `AppTx.Data`, and thus to be decoded in +the `RunTx` method, and used to update the plugin state. -For more examples and inspiration, see our [repository of example plugins](https://github.com/tendermint/basecoin-examples). +For more examples and inspiration, see our [repository of example +plugins](https://github.com/tendermint/basecoin-examples). ## Conclusion In this guide, we demonstrated how to create a new plugin and how to extend the -`basecoin` tool to start a blockchain with the plugin enabled and send transactions to it. -In the next guide, we introduce a [plugin for Inter Blockchain Communication](ibc.md), -which allows us to publish proofs of the state of one blockchain to another, -and thus to transfer tokens and data between them. +`basecoin` tool to start a blockchain with the plugin enabled and send +transactions to it. In the next guide, we introduce a [plugin for Inter +Blockchain Communication](ibc.md), which allows us to publish proofs of the +state of one blockchain to another, and thus to transfer tokens and data +between them. diff --git a/docs/guide/basecoin-tool.md b/docs/guide/basecoin-tool.md index c19b4938c613..0f89f7bd7337 100644 --- a/docs/guide/basecoin-tool.md +++ b/docs/guide/basecoin-tool.md @@ -1,12 +1,14 @@ # The Basecoin Tool -In previous tutorials we learned the [basics of the `basecoin` CLI](/docs/guide/basecoin-basics.md) -and [how to implement a plugin](/docs/guide/basecoin-plugins.md). -In this tutorial, we provide more details on using the `basecoin` tool. +In previous tutorials we learned the [basics of the Basecoin +CLI](/docs/guide/basecoin-basics.md) and [how to implement a +plugin](/docs/guide/basecoin-plugins.md). In this tutorial, we provide more +details on using the Basecoin tool. # Data Directory -By default, `basecoin` works out of `~/.basecoin`. To change this, set the `BCHOME` environment variable: +By default, `basecoin` works out of `~/.basecoin`. To change this, set the +`BCHOME` environment variable: ``` export BCHOME=~/.my_basecoin_data @@ -23,15 +25,16 @@ BCHOME=~/.my_basecoin_data basecoin start # ABCI Server -So far we have run Basecoin and Tendermint in a single process. -However, since we use ABCI, we can actually run them in different processes. -First, initialize them: +So far we have run Basecoin and Tendermint in a single process. However, since +we use ABCI, we can actually run them in different processes. First, +initialize them: ``` basecoin init ``` -This will create a single `genesis.json` file in `~/.basecoin` with the information for both Basecoin and Tendermint. +This will create a single `genesis.json` file in `~/.basecoin` with the +information for both Basecoin and Tendermint. Now, In one window, run @@ -47,7 +50,8 @@ TMROOT=~/.basecoin tendermint node You should see Tendermint start making blocks! -Alternatively, you could ignore the Tendermint details in `~/.basecoin/genesis.json` and use a separate directory by running: +Alternatively, you could ignore the Tendermint details in +`~/.basecoin/genesis.json` and use a separate directory by running: ``` tendermint init @@ -58,9 +62,11 @@ For more details on using `tendermint`, see [the guide](https://tendermint.com/d # Keys and Genesis -In previous tutorials we used `basecoin init` to initialize `~/.basecoin` with the default configuration. -This command creates files both for Tendermint and for Basecoin, and a single `genesis.json` file for both of them. -For more information on these files, see the [guide to using tendermint](https://tendermint.com/docs/guides/using-tendermint). +In previous tutorials we used `basecoin init` to initialize `~/.basecoin` with +the default configuration. This command creates files both for Tendermint and +for Basecoin, and a single `genesis.json` file for both of them. For more +information on these files, see the [guide to using +Tendermint](https://tendermint.com/docs/guides/using-tendermint). Now let's make our own custom Basecoin data. @@ -70,67 +76,88 @@ First, create a new directory: mkdir example-data ``` -We can tell `basecoin` to use this directory by exporting the `BCHOME` environment variable: +We can tell `basecoin` to use this directory by exporting the `BCHOME` +environment variable: ``` export BCHOME=$(pwd)/example-data ``` -If you're going to be using multiple terminal windows, make sure to add this variable to your shell startup scripts (eg. `~/.bashrc`). +If you're going to be using multiple terminal windows, make sure to add this +variable to your shell startup scripts (eg. `~/.bashrc`). -Now, let's create a new private key: +Now, let's create a new key: ``` -basecoin key new > $BCHOME/key.json +basecli keys new foobar ``` -Here's what my `key.json looks like: +The key's info can be retrieved with + +``` +basecli keys get foobar -o=json +``` + +You should get output which looks similar to the following: ```json { - "address": "4EGEhnqOw/gX326c7KARUkY1kic=", - "pub_key": { - "type": "ed25519", - "data": "a20d48b5caff42892d0ac67ccdeee38c1dcbbe42b15b486057d16244541e8141" - }, - "priv_key": { - "type": "ed25519", - "data": "654c845f4b36d1a881deb0ff09381165d3ccd156b4aabb5b51267e91f1d024a5a20d48b5caff42892d0ac67ccdeee38c1dcbbe42b15b486057d16244541e8141" - } + "name": "foobar", + "address": "404C5003A703C7DA888C96A2E901FCE65A6869D9", + "pubkey": { + "type": "ed25519", + "data": "8786B7812AB3B27892D8E14505EEFDBB609699E936F6A4871B1983F210736EEA" + } } ``` -Yours will look different - each key is randomly derrived. - -Now we can make a `genesis.json` file and add an account with our public key: +Yours will look different - each key is randomly derived. Now we can make a +`genesis.json` file and add an account with our public key: ```json { + "app_hash": "", "chain_id": "example-chain", - "app_options": { - "accounts": [{ + "genesis_time": "0001-01-01T00:00:00.000Z", + "validators": [ + { + "amount": 10, + "name": "", "pub_key": { "type": "ed25519", - "data": "a20d48b5caff42892d0ac67ccdeee38c1dcbbe42b15b486057d16244541e8141" - }, - "coins": [ - { - "denom": "gold", - "amount": 1000000000 - } - ] - }] + "data": "7B90EA87E7DC0C7145C8C48C08992BE271C7234134343E8A8E8008E617DE7B30" + } + } + ], + "app_options": { + "accounts": [ + { + "pub_key": { + "type": "ed25519", + "data": "8786B7812AB3B27892D8E14505EEFDBB609699E936F6A4871B1983F210736EEA" + }, + "coins": [ + { + "denom": "gold", + "amount": 1000000000 + } + ] + } + ] } } ``` -Here we've granted ourselves `1000000000` units of the `gold` token. -Note that we've also set the `chain-id` to be `example-chain`. -All transactions must therefore include the `--chain-id example-chain` in order to make sure they are valid for this chain. -Previously, we didn't need this flag because we were using the default chain ID ("test_chain_id"). -Now that we're using a custom chain, we need to specify the chain explicitly on the command line. +Here we've granted ourselves `1000000000` units of the `gold` token. Note that +we've also set the `chain-id` to be `example-chain`. All transactions must +therefore include the `--chain-id example-chain` in order to make sure they are +valid for this chain. Previously, we didn't need this flag because we were +using the default chain ID ("test_chain_id"). Now that we're using a custom +chain, we need to specify the chain explicitly on the command line. -Note we have also left out the details of the tendermint genesis. These are documented in the [tendermint guide](https://tendermint.com/docs/guides/using-tendermint). +Note we have also left out the details of the Tendermint genesis. These are +documented in the [Tendermint +guide](https://tendermint.com/docs/guides/using-tendermint). # Reset @@ -141,13 +168,19 @@ You can reset all blockchain data by running: basecoin unsafe_reset_all ``` +Similarity you can reset client data by running: + +``` +basecli reset_all +``` # Genesis -Any required plugin initialization should be constructed using `SetOption` on genesis. -When starting a new chain for the first time, `SetOption` will be called for each item the genesis file. -Within genesis.json file entries are made in the format: `"/", ""`, where `` is the plugin name, -and `` and `` are the strings passed into the plugin SetOption function. -This function is intended to be used to set plugin specific information such -as the plugin state. +Any required plugin initialization should be constructed using `SetOption` on +genesis. When starting a new chain for the first time, `SetOption` will be +called for each item the genesis file. Within genesis.json file entries are +made in the format: `"/", ""`, where `` is the +plugin name, and `` and `` are the strings passed into the plugin +SetOption function. This function is intended to be used to set plugin +specific information such as the plugin state. diff --git a/cmd/counter/main.go b/docs/guide/counter/cmd/counter/main.go similarity index 58% rename from cmd/counter/main.go rename to docs/guide/counter/cmd/counter/main.go index 11c16f8ebf70..0da66c12f6e9 100644 --- a/cmd/counter/main.go +++ b/docs/guide/counter/cmd/counter/main.go @@ -5,8 +5,11 @@ import ( "github.com/spf13/cobra" - "github.com/tendermint/basecoin/cmd/commands" "github.com/tendermint/tmlibs/cli" + + "github.com/tendermint/basecoin/cmd/commands" + "github.com/tendermint/basecoin/docs/guide/counter/plugins/counter" + "github.com/tendermint/basecoin/types" ) func main() { @@ -22,6 +25,7 @@ func main() { commands.VersionCmd, ) - cmd := cli.PrepareMainCmd(RootCmd, "BC", os.ExpandEnv("$HOME/.basecoin")) + commands.RegisterStartPlugin("counter", func() types.Plugin { return counter.New() }) + cmd := cli.PrepareMainCmd(RootCmd, "CT", os.ExpandEnv("$HOME/.counter")) cmd.Execute() } diff --git a/cmd/countercli/commands/counter.go b/docs/guide/counter/cmd/countercli/commands/counter.go similarity index 96% rename from cmd/countercli/commands/counter.go rename to docs/guide/counter/cmd/countercli/commands/counter.go index 22064e08cba5..0481e3d0654f 100644 --- a/cmd/countercli/commands/counter.go +++ b/docs/guide/counter/cmd/countercli/commands/counter.go @@ -8,7 +8,7 @@ import ( txcmd "github.com/tendermint/light-client/commands/txs" bcmd "github.com/tendermint/basecoin/cmd/basecli/commands" - "github.com/tendermint/basecoin/plugins/counter" + "github.com/tendermint/basecoin/docs/guide/counter/plugins/counter" btypes "github.com/tendermint/basecoin/types" ) diff --git a/cmd/countercli/commands/query.go b/docs/guide/counter/cmd/countercli/commands/query.go similarity index 89% rename from cmd/countercli/commands/query.go rename to docs/guide/counter/cmd/countercli/commands/query.go index 751f7a11b032..692e04c7b3fa 100644 --- a/cmd/countercli/commands/query.go +++ b/docs/guide/counter/cmd/countercli/commands/query.go @@ -5,7 +5,7 @@ import ( proofcmd "github.com/tendermint/light-client/commands/proofs" - "github.com/tendermint/basecoin/plugins/counter" + "github.com/tendermint/basecoin/docs/guide/counter/plugins/counter" ) //CounterQueryCmd CLI command to query the counter state diff --git a/cmd/countercli/main.go b/docs/guide/counter/cmd/countercli/main.go similarity index 62% rename from cmd/countercli/main.go rename to docs/guide/counter/cmd/countercli/main.go index 57204d3fb6b8..7b543d352664 100644 --- a/cmd/countercli/main.go +++ b/docs/guide/counter/cmd/countercli/main.go @@ -14,12 +14,12 @@ import ( "github.com/tendermint/tmlibs/cli" bcmd "github.com/tendermint/basecoin/cmd/basecli/commands" - bcount "github.com/tendermint/basecoin/cmd/countercli/commands" + bcount "github.com/tendermint/basecoin/docs/guide/counter/cmd/countercli/commands" ) // BaseCli represents the base command when called without any subcommands var BaseCli = &cobra.Command{ - Use: "basecli", + Use: "countercli", Short: "Light client for tendermint", Long: `Basecli is an version of tmcli including custom logic to present a nice (not raw hex) interface to the basecoin blockchain structure. @@ -33,21 +33,25 @@ func main() { commands.AddBasicFlags(BaseCli) // Prepare queries - pr := proofs.RootCmd - // These are default parsers, but you optional in your app - pr.AddCommand(proofs.TxCmd) - pr.AddCommand(proofs.KeyCmd) - pr.AddCommand(bcmd.AccountQueryCmd) + proofs.RootCmd.AddCommand( + // These are default parsers, optional in your app + proofs.TxCmd, + proofs.KeyCmd, + bcmd.AccountQueryCmd, - // IMPORTANT: here is how you add custom query commands in your app - pr.AddCommand(bcount.CounterQueryCmd) + // XXX IMPORTANT: here is how you add custom query commands in your app + bcount.CounterQueryCmd, + ) + // Prepare transactions proofs.TxPresenters.Register("base", bcmd.BaseTxPresenter{}) - tr := txs.RootCmd - tr.AddCommand(bcmd.SendTxCmd) + txs.RootCmd.AddCommand( + // This is the default transaction, optional in your app + bcmd.SendTxCmd, - // IMPORTANT: here is how you add custom tx construction for your app - tr.AddCommand(bcount.CounterTxCmd) + // XXX IMPORTANT: here is how you add custom tx construction for your app + bcount.CounterTxCmd, + ) // Set up the various commands to use BaseCli.AddCommand( @@ -55,10 +59,11 @@ func main() { commands.ResetCmd, keycmd.RootCmd, seeds.RootCmd, - pr, - tr, - proxy.RootCmd) + proofs.RootCmd, + txs.RootCmd, + proxy.RootCmd, + ) - cmd := cli.PrepareMainCmd(BaseCli, "BC", os.ExpandEnv("$HOME/.basecli")) + cmd := cli.PrepareMainCmd(BaseCli, "CTL", os.ExpandEnv("$HOME/.countercli")) cmd.Execute() } diff --git a/plugins/counter/counter.go b/docs/guide/counter/plugins/counter/counter.go similarity index 100% rename from plugins/counter/counter.go rename to docs/guide/counter/plugins/counter/counter.go diff --git a/plugins/counter/counter_test.go b/docs/guide/counter/plugins/counter/counter_test.go similarity index 100% rename from plugins/counter/counter_test.go rename to docs/guide/counter/plugins/counter/counter_test.go diff --git a/docs/guide/ibc.md b/docs/guide/ibc.md index 71e196d01bdc..9c30a6d391c9 100644 --- a/docs/guide/ibc.md +++ b/docs/guide/ibc.md @@ -1,29 +1,31 @@ # InterBlockchain Communication with Basecoin -One of the most exciting elements of the Cosmos Network is the InterBlockchain Communication (IBC) protocol, -which enables interoperability across different blockchains. -The simplest example of using the IBC protocol is to send a data packet from one blockchain to another. +One of the most exciting elements of the Cosmos Network is the InterBlockchain +Communication (IBC) protocol, which enables interoperability across different +blockchains. The simplest example of using the IBC protocol is to send a data +packet from one blockchain to another. -We implemented IBC as a basecoin plugin. -and here we'll show you how to use the Basecoin IBC-plugin to send a packet of data across blockchains! +We implemented IBC as a basecoin plugin. and here we'll show you how to use +the Basecoin IBC-plugin to send a packet of data across blockchains! -Please note, this tutorial assumes you are familiar with [Basecoin plugins](/docs/guide/basecoin-plugins.md), -but we'll explain how IBC works. You may also want to see [our repository of example plugins](https://github.com/tendermint/basecoin-examples). +Please note, this tutorial assumes you are familiar with [Basecoin +plugins](/docs/guide/basecoin-plugins.md), but we'll explain how IBC works. You +may also want to see [our repository of example +plugins](https://github.com/tendermint/basecoin-examples). The IBC plugin defines a new set of transactions as subtypes of the `AppTx`. -The plugin's functionality is accessed by setting the `AppTx.Name` field to `"IBC"`, -and setting the `Data` field to the serialized IBC transaction type. +The plugin's functionality is accessed by setting the `AppTx.Name` field to +`"IBC"`, and setting the `Data` field to the serialized IBC transaction type. We'll demonstrate exactly how this works below. ## IBC -Let's review the IBC protocol. -The purpose of IBC is to enable one blockchain to function as a light-client of another. -Since we are using a classical Byzantine Fault Tolerant consensus algorithm, -light-client verification is cheap and easy: -all we have to do is check validator signatures on the latest block, -and verify a Merkle proof of the state. +Let's review the IBC protocol. The purpose of IBC is to enable one blockchain +to function as a light-client of another. Since we are using a classical +Byzantine Fault Tolerant consensus algorithm, light-client verification is +cheap and easy: all we have to do is check validator signatures on the latest +block, and verify a Merkle proof of the state. In Tendermint, validators agree on a block before processing it. This means that the signatures and state root for that block aren't included until the @@ -31,9 +33,9 @@ next block. Thus, each block contains a field called `LastCommit`, which contains the votes responsible for committing the previous block, and a field in the block header called `AppHash`, which refers to the Merkle root hash of the application after processing the transactions from the previous block. So, -if we want to verify the `AppHash` from height H, we need the signatures from `LastCommit` -at height H+1. (And remember that this `AppHash` only contains the results from all -transactions up to and including block H-1) +if we want to verify the `AppHash` from height H, we need the signatures from +`LastCommit` at height H+1. (And remember that this `AppHash` only contains the +results from all transactions up to and including block H-1) Unlike Proof-of-Work, the light-client protocol does not need to download and check all the headers in the blockchain - the client can always jump straight @@ -43,109 +45,94 @@ changes, which requires downloading headers for each block in which there is a significant change. Here, we will assume the validator set is constant, and postpone handling validator set changes for another time. -Now we can describe exactly how IBC works. -Suppose we have two blockchains, `chain1` and `chain2`, and we want to send some data from `chain1` to `chain2`. +Now we can describe exactly how IBC works. Suppose we have two blockchains, +`chain1` and `chain2`, and we want to send some data from `chain1` to `chain2`. We need to do the following: - 1. Register the details (ie. chain ID and genesis configuration) of `chain1` on `chain2` - 2. Within `chain1`, broadcast a transaction that creates an outgoing IBC packet destined for `chain2` - 3. Broadcast a transaction to `chain2` informing it of the latest state (ie. header and commit signatures) of `chain1` - 4. Post the outgoing packet from `chain1` to `chain2`, including the proof that -it was indeed committed on `chain1`. Note `chain2` can only verify this proof -because it has a recent header and commit. - -Each of these steps involves a separate IBC transaction type. Let's take them up in turn. + 1. Register the details (ie. chain ID and genesis configuration) of `chain1` + on `chain2` + 2. Within `chain1`, broadcast a transaction that creates an outgoing IBC + packet destined for `chain2` + 3. Broadcast a transaction to `chain2` informing it of the latest state (ie. + header and commit signatures) of `chain1` + 4. Post the outgoing packet from `chain1` to `chain2`, including the proof + that it was indeed committed on `chain1`. Note `chain2` can only verify +this proof because it has a recent header and commit. + +Each of these steps involves a separate IBC transaction type. Let's take them +up in turn. ### IBCRegisterChainTx -The `IBCRegisterChainTx` is used to register one chain on another. -It contains the chain ID and genesis configuration of the chain to register: +The `IBCRegisterChainTx` is used to register one chain on another. It contains +the chain ID and genesis configuration of the chain to register: -```golang -type IBCRegisterChainTx struct { - BlockchainGenesis -} +```golang type IBCRegisterChainTx struct { BlockchainGenesis } -type BlockchainGenesis struct { - ChainID string - Genesis string -} -``` +type BlockchainGenesis struct { ChainID string Genesis string } ``` -This transaction should only be sent once for a given chain ID, and successive sends will return an error. +This transaction should only be sent once for a given chain ID, and successive +sends will return an error. ### IBCUpdateChainTx -The `IBCUpdateChainTx` is used to update the state of one chain on another. -It contains the header and commit signatures for some block in the chain: +The `IBCUpdateChainTx` is used to update the state of one chain on another. It +contains the header and commit signatures for some block in the chain: -```golang -type IBCUpdateChainTx struct { - Header tm.Header - Commit tm.Commit -} +```golang type IBCUpdateChainTx struct { Header tm.Header Commit tm.Commit } ``` -In the future, it needs to be updated to include changes to the validator set as well. -Anyone can relay an `IBCUpdateChainTx`, and they only need to do so as frequently as packets are being sent or the validator set is changing. +In the future, it needs to be updated to include changes to the validator set +as well. Anyone can relay an `IBCUpdateChainTx`, and they only need to do so +as frequently as packets are being sent or the validator set is changing. ### IBCPacketCreateTx -The `IBCPacketCreateTx` is used to create an outgoing packet on one chain. -The packet itself contains the source and destination chain IDs, -a sequence number (i.e. an integer that increments with every message sent between this pair of chains), -a packet type (e.g. coin, data, etc.), -and a payload. - -```golang -type IBCPacketCreateTx struct { - Packet -} - -type Packet struct { - SrcChainID string - DstChainID string - Sequence uint64 - Type string - Payload []byte -} -``` +The `IBCPacketCreateTx` is used to create an outgoing packet on one chain. The +packet itself contains the source and destination chain IDs, a sequence number +(i.e. an integer that increments with every message sent between this pair of +chains), a packet type (e.g. coin, data, etc.), and a payload. + +```golang type IBCPacketCreateTx struct { Packet } -We have yet to define the format for the payload, so, for now, it's just arbitrary bytes. +type Packet struct { SrcChainID string DstChainID string Sequence uint64 Type +string Payload []byte } ``` -One way to think about this is that `chain2` has an account on `chain1`. -With a `IBCPacketCreateTx` on `chain1`, we send funds to that account. -Then we can prove to `chain2` that there are funds locked up for it in it's -account on `chain1`. -Those funds can only be unlocked with corresponding IBC messages back from -`chain2` to `chain1` sending the locked funds to another account on +We have yet to define the format for the payload, so, for now, it's just +arbitrary bytes. + +One way to think about this is that `chain2` has an account on `chain1`. With +a `IBCPacketCreateTx` on `chain1`, we send funds to that account. Then we can +prove to `chain2` that there are funds locked up for it in it's account on +`chain1`. Those funds can only be unlocked with corresponding IBC messages +back from `chain2` to `chain1` sending the locked funds to another account on `chain1`. ### IBCPacketPostTx -The `IBCPacketPostTx` is used to post an outgoing packet from one chain to another. -It contains the packet and a proof that the packet was committed into the state of the sending chain: +The `IBCPacketPostTx` is used to post an outgoing packet from one chain to +another. It contains the packet and a proof that the packet was committed into +the state of the sending chain: -```golang -type IBCPacketPostTx struct { - FromChainID string // The immediate source of the packet, not always Packet.SrcChainID - FromChainHeight uint64 // The block height in which Packet was committed, to check Proof - Packet - Proof *merkle.IAVLProof -} -``` +```golang type IBCPacketPostTx struct { FromChainID string // The immediate +source of the packet, not always Packet.SrcChainID FromChainHeight uint64 // +The block height in which Packet was committed, to check Proof Packet Proof +*merkle.IAVLProof } ``` + +The proof is a Merkle proof in an IAVL tree, our implementation of a balanced, +Merklized binary search tree. It contains a list of nodes in the tree, which +can be hashed together to get the Merkle root hash. This hash must match the +`AppHash` contained in the header at `FromChainHeight + 1` -The proof is a Merkle proof in an IAVL tree, our implementation of a balanced, Merklized binary search tree. -It contains a list of nodes in the tree, which can be hashed together to get the Merkle root hash. -This hash must match the `AppHash` contained in the header at `FromChainHeight + 1` -- note the `+ 1` is necessary since `FromChainHeight` is the height in which the packet was committed, -and the resulting state root is not included until the next block. +- note the `+ 1` is necessary since `FromChainHeight` is the height in which + the packet was committed, and the resulting state root is not included until +the next block. ### IBC State Now that we've seen all the transaction types, let's talk about the state. -Each chain stores some IBC state in its Merkle tree. -For each chain being tracked by our chain, we store: +Each chain stores some IBC state in its Merkle tree. For each chain being +tracked by our chain, we store: - Genesis configuration - Latest state @@ -154,143 +141,131 @@ For each chain being tracked by our chain, we store: We also store all incoming (ingress) and outgoing (egress) packets. The state of a chain is updated every time an `IBCUpdateChainTx` is committed. -New packets are added to the egress state upon `IBCPacketCreateTx`. -New packets are added to the ingress state upon `IBCPacketPostTx`, -assuming the proof checks out. +New packets are added to the egress state upon `IBCPacketCreateTx`. New +packets are added to the ingress state upon `IBCPacketPostTx`, assuming the +proof checks out. ## Merkle Queries -The Basecoin application uses a single Merkle tree that is shared across all its state, -including the built-in accounts state and all plugin state. For this reason, -it's important to use explicit key names and/or hashes to ensure there are no collisions. +The Basecoin application uses a single Merkle tree that is shared across all +its state, including the built-in accounts state and all plugin state. For this +reason, it's important to use explicit key names and/or hashes to ensure there +are no collisions. -We can query the Merkle tree using the ABCI Query method. -If we pass in the correct key, it will return the corresponding value, -as well as a proof that the key and value are contained in the Merkle tree. +We can query the Merkle tree using the ABCI Query method. If we pass in the +correct key, it will return the corresponding value, as well as a proof that +the key and value are contained in the Merkle tree. The results of a query can thus be used as proof in an `IBCPacketPostTx`. ## Try it out -Now that we have all the background knowledge, let's actually walk through the tutorial. +Now that we have all the background knowledge, let's actually walk through the +tutorial. Make sure you have installed [Tendermint](https://tendermint.com/intro/getting-started/download) and [basecoin](/docs/guide/install.md). -`basecoin` is a framework for creating new cryptocurrency applications. -It comes with an `IBC` plugin enabled by default. +`basecoin` is a framework for creating new cryptocurrency applications. It +comes with an `IBC` plugin enabled by default. -You will also want to install the [jq](https://stedolan.github.io/jq/) for handling JSON at the command line. +You will also want to install the [jq](https://stedolan.github.io/jq/) for +handling JSON at the command line. -Now let's start the two blockchains. -In this tutorial, each chain will have only a single validator, -where the initial configuration files are already generated. -Let's change directory so these files are easily accessible: +Now let's start the two blockchains. In this tutorial, each chain will have +only a single validator, where the initial configuration files are already +generated. Let's change directory so these files are easily accessible: -``` -cd $GOPATH/src/github.com/tendermint/basecoin/demo -``` +``` cd $GOPATH/src/github.com/tendermint/basecoin/demo ``` -The relevant data is now in the `data` directory. -Before we begin, let's set some environment variables for convenience: +The relevant data is now in the `data` directory. Before we begin, let's set +some environment variables for convenience: -``` -export BCHOME="." -BCHOME1="./data/chain1" -BCHOME2="./data/chain2" +``` export BCHOME="." BCHOME1="./data/chain1" BCHOME2="./data/chain2" -export CHAIN_ID1=test_chain_1 -export CHAIN_ID2=test_chain_2 +export CHAIN_ID1=test_chain_1 export CHAIN_ID2=test_chain_2 CHAIN_FLAGS1="--chain_id $CHAIN_ID1 --from $BCHOME1/key.json" -CHAIN_FLAGS2="--chain_id $CHAIN_ID2 --from $BCHOME2/key.json --node tcp://localhost:36657" -``` +CHAIN_FLAGS2="--chain_id $CHAIN_ID2 --from $BCHOME2/key.json --node +tcp://localhost:36657" ``` -In previous examples, we started basecoin in-process with tendermint. -Here, we will run them in different processes, using the `--without-tendermint` flag, -as described in the [guide to the basecoin tool](basecoin-tool.md). -We can start the two chains as follows: +In previous examples, we started basecoin in-process with tendermint. Here, we +will run them in different processes, using the `--without-tendermint` flag, as +described in the [guide to the basecoin tool](basecoin-tool.md). We can start +the two chains as follows: -``` -TMROOT=$BCHOME1 tendermint node --log_level=info &> chain1_tendermint.log & +``` TMROOT=$BCHOME1 tendermint node --log_level=info &> chain1_tendermint.log & BCHOME=$BCHOME1 basecoin start --without-tendermint &> chain1_basecoin.log & ``` and -``` -TMROOT=$BCHOME2 tendermint node --log_level=info --node_laddr tcp://localhost:36656 --rpc_laddr tcp://localhost:36657 --proxy_app tcp://localhost:36658 &> chain2_tendermint.log & -BCHOME=$BCHOME2 basecoin start --address tcp://localhost:36658 --without-tendermint &> chain2_basecoin.log & +``` TMROOT=$BCHOME2 tendermint node --log_level=info --node_laddr +tcp://localhost:36656 --rpc_laddr tcp://localhost:36657 --proxy_app +tcp://localhost:36658 &> chain2_tendermint.log & BCHOME=$BCHOME2 basecoin start +--address tcp://localhost:36658 --without-tendermint &> chain2_basecoin.log & ``` -Note how we refer to the relevant data directories, and how we set the various addresses for the second node so as not to conflict with the first. +Note how we refer to the relevant data directories, and how we set the various +addresses for the second node so as not to conflict with the first. We can now check on the status of the two chains: -``` -curl localhost:46657/status -curl localhost:36657/status -``` +``` curl localhost:46657/status curl localhost:36657/status ``` -If either command fails, the nodes may not have finished starting up. Wait a couple seconds and try again. -Once you see the status of both chains, it's time to move on. +If either command fails, the nodes may not have finished starting up. Wait a +couple seconds and try again. Once you see the status of both chains, it's +time to move on. -In this tutorial, we're going to send some data from `test_chain_1` to `test_chain_2`. -We begin by registering `test_chain_1` on `test_chain_2`: +In this tutorial, we're going to send some data from `test_chain_1` to +`test_chain_2`. We begin by registering `test_chain_1` on `test_chain_2`: -``` -basecoin tx ibc --amount 10mycoin $CHAIN_FLAGS2 register --ibc_chain_id $CHAIN_ID1 --genesis $BCHOME1/genesis.json -``` +``` basecoin tx ibc --amount 10mycoin $CHAIN_FLAGS2 register --ibc_chain_id +$CHAIN_ID1 --genesis $BCHOME1/genesis.json ``` Now we can create the outgoing packet on `test_chain_1`: -``` -basecoin tx ibc --amount 10mycoin $CHAIN_FLAGS1 packet create --ibc_from $CHAIN_ID1 --to $CHAIN_ID2 --type coin --payload 0xDEADBEEF --ibc_sequence 1 +``` basecoin tx ibc --amount 10mycoin $CHAIN_FLAGS1 packet create --ibc_from +$CHAIN_ID1 --to $CHAIN_ID2 --type coin --payload 0xDEADBEEF --ibc_sequence 1 ``` -Note our payload is just `DEADBEEF`. -Now that the packet is committed in the chain, let's get some proof by querying: +Note our payload is just `DEADBEEF`. Now that the packet is committed in the +chain, let's get some proof by querying: -``` -QUERY=$(basecoin query ibc,egress,$CHAIN_ID1,$CHAIN_ID2,1) -echo $QUERY -``` +``` QUERY=$(basecoin query ibc,egress,$CHAIN_ID1,$CHAIN_ID2,1) echo $QUERY ``` -The result contains the latest height, a value (i.e. the hex-encoded binary serialization of our packet), -and a proof (i.e. hex-encoded binary serialization of a list of nodes from the Merkle tree) that the value is in the Merkle tree. -We keep the result in the `QUERY` variable so we can easily reference subfields using the `jq` tool. +The result contains the latest height, a value (i.e. the hex-encoded binary +serialization of our packet), and a proof (i.e. hex-encoded binary +serialization of a list of nodes from the Merkle tree) that the value is in the +Merkle tree. We keep the result in the `QUERY` variable so we can easily +reference subfields using the `jq` tool. -If we want to send this data to `test_chain_2`, we first have to update what it knows about `test_chain_1`. -We'll need a recent block header and a set of commit signatures. -Fortunately, we can get them with the `block` command: +If we want to send this data to `test_chain_2`, we first have to update what it +knows about `test_chain_1`. We'll need a recent block header and a set of +commit signatures. Fortunately, we can get them with the `block` command: -``` -BLOCK=$(basecoin block $(echo $QUERY | jq .height)) -echo $BLOCK -``` +``` BLOCK=$(basecoin block $(echo $QUERY | jq .height)) echo $BLOCK ``` Here, we are passing `basecoin block` the `height` from our earlier query. -Note the result contains both a hex-encoded and json-encoded version of the header and the commit. -The former is used as input for later commands; the latter is human-readable, so you know what's going on! +Note the result contains both a hex-encoded and json-encoded version of the +header and the commit. The former is used as input for later commands; the +latter is human-readable, so you know what's going on! Let's send this updated information about `test_chain_1` to `test_chain_2`. First, output the header and commit for reference: -``` -echo $BLOCK | jq .hex.header -echo $BLOCK | jq .hex.commit -``` +``` echo $BLOCK | jq .hex.header echo $BLOCK | jq .hex.commit ``` And now forward those values to `test_chain_2`: -``` -basecoin tx ibc --amount 10mycoin $CHAIN_FLAGS2 update --header 0x
--commit 0x -``` +``` basecoin tx ibc --amount 10mycoin $CHAIN_FLAGS2 update --header 0x
+--commit 0x ``` -Now that `test_chain_2` knows about some recent state of `test_chain_1`, we can post the packet to `test_chain_2`, -along with proof the packet was committed on `test_chain_1`. Since `test_chain_2` knows about some recent state -of `test_chain_1`, it will be able to verify the proof! +Now that `test_chain_2` knows about some recent state of `test_chain_1`, we can +post the packet to `test_chain_2`, along with proof the packet was committed on +`test_chain_1`. Since `test_chain_2` knows about some recent state of +`test_chain_1`, it will be able to verify the proof! First, output the height, packet, and proof for reference: @@ -303,18 +278,22 @@ echo $QUERY | jq .proof And forward those values to `test_chain_2`: ``` -basecoin tx ibc --amount 10mycoin $CHAIN_FLAGS2 packet post --ibc_from $CHAIN_ID1 --height --packet 0x --proof 0x +basecoin tx ibc --amount=10mycoin $CHAIN_FLAGS2 packet post --ibc_from $CHAIN_ID1 --height --packet 0x --proof 0x ``` -If the command does not return an error, then we have successfuly transfered data from `test_chain_1` to `test_chain_2`. Tada! +If the command does not return an error, then we have successfuly transfered +data from `test_chain_1` to `test_chain_2`. Tada! ## Conclusion -In this tutorial we explained how IBC works, and demonstrated how to use it to communicate between two chains. -We did the simplest communciation possible: a one way transfer of data from chain1 to chain2. -The most important part was that we updated chain2 with the latest state (i.e. header and commit) of chain1, -and then were able to post a proof to chain2 that a packet was committed to the outgoing state of chain1. +In this tutorial we explained how IBC works, and demonstrated how to use it to +communicate between two chains. We did the simplest communciation possible: a +one way transfer of data from chain1 to chain2. The most important part was +that we updated chain2 with the latest state (i.e. header and commit) of +chain1, and then were able to post a proof to chain2 that a packet was +committed to the outgoing state of chain1. -In a future tutorial, we will demonstrate how to use IBC to actually transfer tokens between two blockchains, -but we'll do it with real testnets deployed across multiple nodes on the network. Stay tuned! +In a future tutorial, we will demonstrate how to use IBC to actually transfer +tokens between two blockchains, but we'll do it with real testnets deployed +across multiple nodes on the network. Stay tuned! diff --git a/docs/guide/install.md b/docs/guide/install.md index 9c5738a77329..59e45bd2ef6e 100644 --- a/docs/guide/install.md +++ b/docs/guide/install.md @@ -3,7 +3,8 @@ On a good day, basecoin can be installed like a normal Go program: ``` -go get -u github.com/tendermint/basecoin/cmd/basecoin +go get github.com/tendermint/basecoin/ +make all ``` In some cases, if that fails, or if another branch is required, @@ -15,7 +16,7 @@ the correct way to install is: cd $GOPATH/src/github.com/tendermint/basecoin git pull origin master make get_vendor_deps -make install +make all ``` This will create the `basecoin` binary in `$GOPATH/bin`.