cosmos-sdk/docs/guide/basecoin-plugins.md

258 lines
10 KiB
Markdown
Raw Normal View History

2017-06-29 00:04:20 -07:00
<!--- shelldown script template, see github.com/rigelrozanski/shelldown
#!/bin/bash
testTutorial_BasecoinPlugins() {
2017-07-04 05:10:06 -07:00
2017-06-29 00:04:20 -07:00
#Initialization
#shelldown[0][1]
#shelldown[0][2]
KEYPASS=qwertyuiop
2017-07-04 05:10:06 -07:00
#Making Keys
2017-06-29 00:04:20 -07:00
RES=$((echo $KEYPASS; echo $KEYPASS) | #shelldown[0][4])
assertTrue "Line $LINENO: Expected to contain safe, got $RES" '[[ $RES == *safe* ]]'
RES=$((echo $KEYPASS; echo $KEYPASS) | #shelldown[0][5])
assertTrue "Line $LINENO: Expected to contain safe, got $RES" '[[ $RES == *safe* ]]'
2017-07-04 05:10:06 -07:00
2017-06-29 00:04:20 -07:00
#shelldown[0][7] >/dev/null
assertTrue "Expected true for line $LINENO" $?
2017-07-04 05:10:06 -07:00
2017-06-29 00:04:20 -07:00
#shelldown[0][9] >>/dev/null 2>&1 &
sleep 5
PID_SERVER=$!
disown
2017-07-04 05:10:06 -07:00
2017-06-29 00:04:20 -07:00
RES=$((echo y) | #shelldown[1][0] $1)
assertTrue "Line $LINENO: Expected to contain validator, got $RES" '[[ $RES == *validator* ]]'
2017-07-04 05:10:06 -07:00
2017-06-29 00:04:20 -07:00
#shelldown[1][2]
assertTrue "Expected true for line $LINENO" $?
RES=$((echo $KEYPASS) | #shelldown[1][3] | jq '.deliver_tx.code')
assertTrue "Line $LINENO: Expected 0 code deliver_tx, got $RES" '[[ $RES == 0 ]]'
2017-07-04 05:10:06 -07:00
2017-06-29 00:04:20 -07:00
RES=$((echo $KEYPASS) | #shelldown[2][0])
assertTrue "Line $LINENO: Expected to contain Valid error, got $RES" \
2017-07-04 05:10:06 -07:00
'[[ $RES == *"Counter Tx marked invalid"* ]]'
2017-06-29 00:04:20 -07:00
RES=$((echo $KEYPASS) | #shelldown[2][1] | jq '.deliver_tx.code')
assertTrue "Line $LINENO: Expected 0 code deliver_tx, got $RES" '[[ $RES == 0 ]]'
2017-07-04 05:10:06 -07:00
RES=$(#shelldown[3][-1] | jq '.data.counter')
2017-06-29 00:04:20 -07:00
assertTrue "Line $LINENO: Expected Counter of 1, got $RES" '[[ $RES == 1 ]]'
RES=$((echo $KEYPASS) | #shelldown[4][0] | jq '.deliver_tx.code')
assertTrue "Line $LINENO: Expected 0 code deliver_tx, got $RES" '[[ $RES == 0 ]]'
RES=$(#shelldown[4][1])
2017-07-04 05:10:06 -07:00
RESCOUNT=$(printf "$RES" | jq '.data.counter')
RESFEE=$(printf "$RES" | jq '.data.total_fees[0].amount')
2017-06-29 00:04:20 -07:00
assertTrue "Line $LINENO: Expected Counter of 2, got $RES" '[[ $RESCOUNT == 2 ]]'
assertTrue "Line $LINENO: Expected TotalFees of 2, got $RES" '[[ $RESFEE == 2 ]]'
}
oneTimeTearDown() {
kill -9 $PID_SERVER >/dev/null 2>&1
sleep 1
}
# load and run these tests with shunit2!
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" #get this files directory
. $DIR/shunit2
-->
2017-04-26 21:51:44 -07:00
# Basecoin Plugins
2017-06-18 16:01:54 -07:00
In the [previous guide](basecoin-basics.md), we saw how to use the `basecoin`
2017-06-29 00:04:20 -07:00
tool to start a blockchain and the `basecli` tools to 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 tools
to use another transaction type, the `AppTx`, so we can send data to a custom
plugin. In this example we explore a simple plugin named `counter`.
2017-04-26 21:51:44 -07:00
## Example Plugin
2017-06-20 04:21:06 -07:00
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) and run `make install` 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`.
2017-06-18 16:01:54 -07:00
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.
2017-06-18 16:01:54 -07:00
2017-06-20 18:36:04 -07:00
A new blockchain can be initialized and started just like in the [previous
2017-06-18 16:01:54 -07:00
guide](basecoin-basics.md):
2017-04-26 21:51:44 -07:00
2017-06-29 00:04:20 -07:00
```shelldown[0]
# WARNING: this wipes out data - but counter is only for demos...
rm -rf ~/.counter
countercli reset_all
2017-06-18 16:01:54 -07:00
countercli keys new cool
countercli keys new friend
2017-04-26 21:51:44 -07:00
2017-06-20 18:36:04 -07:00
counter init $(countercli keys get cool | awk '{print $2}')
2017-04-26 21:51:44 -07:00
2017-06-18 16:01:54 -07:00
counter start
2017-04-26 21:51:44 -07:00
```
2017-06-18 16:01:54 -07:00
The default files are stored in `~/.counter`. In another window we can
initialize the light-client and send a transaction:
2017-04-26 21:51:44 -07:00
2017-06-29 00:04:20 -07:00
```shelldown[1]
countercli init --node=tcp://localhost:46657 --genesis=$HOME/.counter/genesis.json
2017-06-18 16:01:54 -07:00
2017-06-20 18:36:04 -07:00
YOU=$(countercli keys get friend | awk '{print $2}')
countercli tx send --name=cool --amount=1000mycoin --to=$YOU --sequence=1
2017-04-26 21:51:44 -07:00
```
2017-06-18 16:01:54 -07:00
But the Counter has an additional command, `countercli tx counter`, which
crafts an `AppTx` specifically for this plugin:
2017-04-26 21:51:44 -07:00
2017-06-29 00:04:20 -07:00
```shelldown[2]
2017-07-04 05:10:06 -07:00
countercli tx counter --name cool --sequence=2
countercli tx counter --name cool --sequence=3 --valid
2017-04-26 21:51:44 -07:00
```
2017-06-18 16:01:54 -07:00
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:
2017-04-26 21:51:44 -07:00
2017-06-29 00:04:20 -07:00
```shelldown[3]
2017-06-18 16:01:54 -07:00
countercli query counter
2017-04-26 21:51:44 -07:00
```
2017-06-18 16:01:54 -07:00
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:
2017-04-26 21:51:44 -07:00
2017-06-29 00:04:20 -07:00
```shelldown[4]
2017-07-04 05:10:06 -07:00
countercli tx counter --name cool --countfee=2mycoin --sequence=4 --valid
2017-06-18 16:01:54 -07:00
countercli query counter
2017-04-26 21:51:44 -07:00
```
2017-06-20 18:36:04 -07:00
The Counter value should be 2, because we sent a second valid transaction.
And this time, since we sent a countfee (which must be less than or equal to the
total amount sent with the tx), it stores the `TotalFees` on the counter as well.
2017-06-20 04:21:06 -07:00
Keep it mind that, just like with `basecli`, the `countercli` verifies a proof
2017-06-20 18:36:04 -07:00
that the query response is correct and up-to-date.
2017-04-26 21:51:44 -07:00
2017-06-18 16:01:54 -07:00
Now, before we implement our own plugin and tooling, it helps to understand the
`AppTx` and the design of the plugin system.
2017-04-26 21:51:44 -07:00
## AppTx
2017-06-18 16:01:54 -07:00
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.
2017-04-26 21:51:44 -07:00
```golang
type AppTx struct {
Gas int64 `json:"gas"`
Fee Coin `json:"fee"`
2017-04-26 21:51:44 -07:00
Input TxInput `json:"input"`
Name string `json:"type"` // Name of the plugin
Data []byte `json:"data"` // Data for the plugin to process
}
```
2017-06-18 16:01:54 -07:00
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.
2017-04-26 21:51:44 -07:00
2017-06-18 16:01:54 -07:00
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.
2017-04-26 21:51:44 -07:00
## Plugins
A plugin is simply a Go package that implements the `Plugin` interface:
```golang
type Plugin interface {
// Name of this plugin, should be short.
Name() string
// Run a transaction from ABCI DeliverTx
RunTx(store KVStore, ctx CallContext, txBytes []byte) (res abci.Result)
// Other ABCI message handlers
SetOption(store KVStore, key string, value string) (log string)
InitChain(store KVStore, vals []*abci.Validator)
BeginBlock(store KVStore, hash []byte, header *abci.Header)
EndBlock(store KVStore, height uint64) (res abci.ResponseEndBlock)
}
type CallContext struct {
CallerAddress []byte // Caller's Address (hash of PubKey)
CallerAccount *Account // Caller's Account, w/ fee & TxInputs deducted
Coins Coins // The coins that the caller wishes to spend, excluding fees
}
```
2017-06-18 16:01:54 -07:00
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`.
2017-04-26 21:51:44 -07:00
2017-06-18 16:01:54 -07:00
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!
2017-04-26 21:51:44 -07:00
2017-06-18 16:01:54 -07:00
For details on how to initialize the state using `SetOption`, see the [guide to
using the basecoin tool](basecoin-tool.md#genesis).
2017-04-26 21:51:44 -07:00
## Implement your own
2017-06-18 16:01:54 -07:00
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. You must also register your plugin(s) with
the basecoin app with `RegisterStartPlugin`.
2017-04-26 21:51:44 -07:00
2017-06-20 04:21:06 -07:00
The light-client is located in `cmd/countercli/main.go` and allows for
2017-06-20 18:36:04 -07:00
transaction and query commands. This file can also be left mostly alone besides replacing the application name and adding
references to new plugin commands.
2017-04-26 21:51:44 -07:00
2017-06-20 18:36:04 -07:00
Next is the custom commands in `cmd/countercli/commands/`. These files are
2017-06-18 16:01:54 -07:00
where we extend the tool with any new commands and flags we need to send
transactions or queries to our plugin. You define custom `tx` and `query`
subcommands, which are registered in `main.go` (avoiding `init()`
auto-registration, for less magic and more control in the main executable).
2017-04-26 21:51:44 -07:00
2017-06-18 16:01:54 -07:00
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.
2017-04-26 21:51:44 -07:00
2017-06-18 16:01:54 -07:00
For more examples and inspiration, see our [repository of example
plugins](https://github.com/tendermint/basecoin-examples).
2017-04-26 21:51:44 -07:00
## Conclusion
In this guide, we demonstrated how to create a new plugin and how to extend the
2017-06-18 16:01:54 -07:00
`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.