update docs, move counter
int int int
This commit is contained in:
parent
7dad89b152
commit
24bd0f5ed6
1
Makefile
1
Makefile
@ -9,6 +9,7 @@ build:
|
||||
|
||||
install:
|
||||
go install ./cmd/...
|
||||
go install ./docs/guide/counter/cmd/...
|
||||
|
||||
dist:
|
||||
@bash scripts/dist.sh
|
||||
|
||||
@ -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"
|
||||
|
||||
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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() })
|
||||
}
|
||||
@ -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 `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.
|
||||
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.
|
||||
|
||||
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`.
|
||||
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
|
||||
|
||||
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.
|
||||
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`.
|
||||
|
||||
For more examples and inspiration, see our [repository of example plugins](https://github.com/tendermint/basecoin-examples).
|
||||
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).
|
||||
|
||||
## 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.
|
||||
|
||||
@ -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"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
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:
|
||||
|
||||
```json
|
||||
{
|
||||
"chain_id": "example-chain",
|
||||
"app_options": {
|
||||
"accounts": [{
|
||||
"pub_key": {
|
||||
"type": "ed25519",
|
||||
"data": "a20d48b5caff42892d0ac67ccdeee38c1dcbbe42b15b486057d16244541e8141"
|
||||
},
|
||||
"coins": [
|
||||
{
|
||||
"denom": "gold",
|
||||
"amount": 1000000000
|
||||
}
|
||||
]
|
||||
}]
|
||||
"name": "foobar",
|
||||
"address": "404C5003A703C7DA888C96A2E901FCE65A6869D9",
|
||||
"pubkey": {
|
||||
"type": "ed25519",
|
||||
"data": "8786B7812AB3B27892D8E14505EEFDBB609699E936F6A4871B1983F210736EEA"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
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.
|
||||
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:
|
||||
|
||||
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).
|
||||
```json
|
||||
{
|
||||
"app_hash": "",
|
||||
"chain_id": "example-chain",
|
||||
"genesis_time": "0001-01-01T00:00:00.000Z",
|
||||
"validators": [
|
||||
{
|
||||
"amount": 10,
|
||||
"name": "",
|
||||
"pub_key": {
|
||||
"type": "ed25519",
|
||||
"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.
|
||||
|
||||
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: `"<plugin>/<key>", "<value>"`, where `<plugin>` is the plugin name,
|
||||
and `<key>` and `<value>` 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: `"<plugin>/<key>", "<value>"`, where `<plugin>` is the
|
||||
plugin name, and `<key>` and `<value>` 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.
|
||||
|
||||
|
||||
@ -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()
|
||||
}
|
||||
@ -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"
|
||||
)
|
||||
|
||||
@ -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
|
||||
@ -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()
|
||||
}
|
||||
@ -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.
|
||||
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.
|
||||
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.
|
||||
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
|
||||
}
|
||||
```golang type IBCPacketCreateTx struct { Packet }
|
||||
|
||||
type Packet struct {
|
||||
SrcChainID string
|
||||
DstChainID string
|
||||
Sequence uint64
|
||||
Type string
|
||||
Payload []byte
|
||||
}
|
||||
```
|
||||
type Packet struct { SrcChainID string DstChainID string Sequence uint64 Type
|
||||
string Payload []byte } ```
|
||||
|
||||
We have yet to define the format for the payload, so, for now, it's just arbitrary bytes.
|
||||
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
|
||||
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`
|
||||
- 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.
|
||||
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.
|
||||
|
||||
### 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<header> --commit 0x<commit>
|
||||
```
|
||||
``` basecoin tx ibc --amount 10mycoin $CHAIN_FLAGS2 update --header 0x<header>
|
||||
--commit 0x<commit> ```
|
||||
|
||||
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 <height> --packet 0x<packet> --proof 0x<proof>
|
||||
basecoin tx ibc --amount=10mycoin $CHAIN_FLAGS2 packet post --ibc_from $CHAIN_ID1 --height <height> --packet 0x<packet> --proof 0x<proof>
|
||||
```
|
||||
|
||||
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!
|
||||
|
||||
|
||||
@ -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`.
|
||||
|
||||
Loading…
Reference in New Issue
Block a user