diff --git a/docs/guide/basecoin-basics.md b/docs/guide/basecoin-basics.md index 9ecb08c44..af3cd8308 100644 --- a/docs/guide/basecoin-basics.md +++ b/docs/guide/basecoin-basics.md @@ -1,7 +1,8 @@ # Basecoin Basics Here we explain how to get started with a simple Basecoin blockchain, -and how to send transactions between accounts using the `basecoin` tool. +how to send transactions between accounts using the `basecoin` tool, +and what is happening under the hood. ## Install @@ -80,14 +81,93 @@ basecoin tx send --to 0x1B1BE55F969F54064628A63B9559E7C21C925165 --from key2.jso See `basecoin tx send --help` for additional details. -## Plugins +For a better understanding of the options, it helps to understand the underlying data structures. -The `tx send` command creates and broadcasts a transaction of type `SendTx`, -which is only useful for moving tokens around. -Fortunately, Basecoin supports another transaction type, the `AppTx`, -which can trigger code registered via a plugin system. +## Accounts -In the [next tutorial](example-plugin.md), -we demonstrate how to implement a plugin -and extend the CLI to support new transaction types! -But first, you may want to learn a bit more about [Basecoin's design](basecoin-design.md) +The Basecoin state consists entirely of a set of accounts. +Each account contains an ED25519 public key, +a balance in many different coin denominations, +and a strictly increasing sequence number for replay protection. +This type of account was directly inspired by accounts in Ethereum, +and is unlike Bitcoin's use of Unspent Transaction Outputs (UTXOs). +Note Basecoin is a multi-asset cryptocurrency, so each account can have many different kinds of tokens. + +```golang +type Account struct { + PubKey crypto.PubKey `json:"pub_key"` // May be nil, if not known. + Sequence int `json:"sequence"` + Balance Coins `json:"coins"` +} + +type Coins []Coin + +type Coin struct { + Denom string `json:"denom"` + Amount int64 `json:"amount"` +} +``` + +Accounts are serialized and stored in a Merkle tree under the key `base/a/
`, where `
` is the address of the account. +Typically, the address of the account is the 20-byte `RIPEMD160` hash of the public key, but other formats are acceptable as well, +as defined in the [tendermint crypto library](https://github.com/tendermint/go-crypto). +The Merkle tree used in Basecoin is a balanced, binary search tree, which we call an [IAVL tree](https://github.com/tendermint/go-merkle). + +## Transactions + +Basecoin defines a simple transaction type, the `SendTx`, which allows tokens to be sent to other accounts. +The `SendTx` takes a list of inputs and a list of outputs, +and transfers all the tokens listed in the inputs from their corresponding accounts to the accounts listed in the output. +The `SendTx` is structured as follows: + +```golang +type SendTx struct { + Gas int64 `json:"gas"` + Fee Coin `json:"fee"` + Inputs []TxInput `json:"inputs"` + Outputs []TxOutput `json:"outputs"` +} + +type TxInput struct { + Address []byte `json:"address"` // Hash of the PubKey + Coins Coins `json:"coins"` // + Sequence int `json:"sequence"` // Must be 1 greater than the last committed TxInput + Signature crypto.Signature `json:"signature"` // Depends on the PubKey type and the whole Tx + PubKey crypto.PubKey `json:"pub_key"` // Is present iff Sequence == 0 +} + +type TxOutput struct { + Address []byte `json:"address"` // Hash of the PubKey + Coins Coins `json:"coins"` // +} +``` + +Note the `SendTx` includes a field for `Gas` and `Fee`. +The `Gas` limits the total amount of computation that can be done by the transaction, +while the `Fee` refers to the total amount paid in fees. +This is slightly different from Ethereum's concept of `Gas` and `GasPrice`, +where `Fee = Gas x GasPrice`. In Basecoin, the `Gas` and `Fee` are independent, +and the `GasPrice` is implicit. + +In Tendermint, the `Fee` is meant to be used by the validators to inform the ordering +of transactions, like in bitcoin. And the `Gas` is meant to be used by the application +plugin to control its execution. There is currently no means to pass `Fee` information +to the Tendermint validators, but it will come soon... + +Note also that the `PubKey` only needs to be sent for `Sequence == 0`. +After that, it is stored under the account in the Merkle tree and subsequent transactions can exclude it, +using only the `Address` to refer to the sender. Ethereum does not require public keys to be sent in transactions +as it uses a different elliptic curve scheme which enables the public key to be derived from the signature itself. + +Finally, note that the use of multiple inputs and multiple outputs allows us to send many +different types of tokens between many different accounts at once in an atomic transaction. +Thus, the `SendTx` can serve as a basic unit of decentralized exchange. When using multiple +inputs and outputs, you must make sure that the sum of coins of the inputs equals the sum of +coins of the outputs (no creating money), and that all accounts that provide inputs have signed the transaction. + +## Conclusion + +In this guide, we introduced the `basecoin` tool, demonstrated how to use it to send tokens between accounts, +and discussed the underlying data types for accounts and transactions, specifically the `Account` and the `SendTx`. +In the [next guide](basecoin-plugins.md), we introduce the basecoin plugin system, which uses a new transaction type, the `AppTx`, +to extend the functionality of the Basecoin system with arbitrary logic. diff --git a/docs/guide/basecoin-design.md b/docs/guide/basecoin-design.md deleted file mode 100644 index a9bf5d503..000000000 --- a/docs/guide/basecoin-design.md +++ /dev/null @@ -1,95 +0,0 @@ -# Basecoin Design - -Basecoin is designed to be a simple cryptocurrency application with limited built-in functionality, -but with the capacity to be extended by arbitrary plugins. -Its basic data structures are inspired by Ethereum, but it is much simpler, as there is no built-in virtual machine. - -## Accounts - -The Basecoin state consists entirely of a set of accounts. -Each account contains an ED25519 public key, -a balance in many different coin denominations, -and a strictly increasing sequence number for replay protection. -This type of account was directly inspired by accounts in Ethereum, -and is unlike Bitcoin's use of Unspent Transaction Outputs (UTXOs). -Note Basecoin is a multi-asset cryptocurrency, so each account can have many different kinds of tokens. - -```golang -type Account struct { - PubKey crypto.PubKey `json:"pub_key"` // May be nil, if not known. - Sequence int `json:"sequence"` - Balance Coins `json:"coins"` -} - -type Coins []Coin - -type Coin struct { - Denom string `json:"denom"` - Amount int64 `json:"amount"` -} -``` - -Accounts are serialized and stored in a Merkle tree using the account's address as the key, -In particular, an account is stored in the Merkle tree under the key `base/a/
`, -where `
` is the address of the account. -In Basecoin, the address of an account is the 20-byte `RIPEMD160` hash of the public key. -The Merkle tree used in Basecoin is a balanced, binary search tree, which we call an [IAVL tree](https://github.com/tendermint/go-merkle). - -## Transactions - -Basecoin defines a simple transaction type, the `SendTx`, which allows tokens to be sent to other accounts. -The `SendTx` takes a list of inputs and a list of outputs, -and transfers all the tokens listed in the inputs from their corresponding accounts to the accounts listed in the output. -The `SendTx` is structured as follows: - -```golang -type SendTx struct { - Gas int64 `json:"gas"` - Fee Coin `json:"fee"` - Inputs []TxInput `json:"inputs"` - Outputs []TxOutput `json:"outputs"` -} - -type TxInput struct { - Address []byte `json:"address"` // Hash of the PubKey - Coins Coins `json:"coins"` // - Sequence int `json:"sequence"` // Must be 1 greater than the last committed TxInput - Signature crypto.Signature `json:"signature"` // Depends on the PubKey type and the whole Tx - PubKey crypto.PubKey `json:"pub_key"` // Is present iff Sequence == 0 -} - -type TxOutput struct { - Address []byte `json:"address"` // Hash of the PubKey - Coins Coins `json:"coins"` // -} -``` - -There are a few things to note. First, the `SendTx` includes a field for `Gas` and `Fee`. -The `Gas` limits the total amount of computation that can be done by the transaction, -while the `Fee` refers to the total amount paid in fees. -This is slightly different from Ethereum's concept of `Gas` and `GasPrice`, -where `Fee = Gas x GasPrice`. In Basecoin, the `Gas` and `Fee` are independent, -and the `GasPrice` is implicit. - -In Tendermint, the `Fee` is meant to be used by the validators to inform the ordering -of transactions, like in bitcoin. And the `Gas` is meant to be used by the application -plugin to control its execution. There is currently no means to pass `Fee` information -to the Tendermint validators, but it will come soon... - -Second, notice that the `PubKey` only needs to be sent for `Sequence == 0`. -After that, it is stored under the account in the Merkle tree and subsequent transactions can exclude it, -using only the `Address` to refer to the sender. Ethereum does not require public keys to be sent in transactions -as it uses a different elliptic curve scheme which enables the public key to be derived from the signature itself. - -Finally, note that the use of multiple inputs and multiple outputs allows us to send many -different types of tokens between many different accounts at once in an atomic transaction. -Thus, the `SendTx` can serve as a basic unit of decentralized exchange. When using multiple -inputs and outputs, you must make sure that the sum of coins of the inputs equals the sum of -coins of the outputs (no creating money), and that all accounts that provide inputs have signed the transaction. - -## Plugins - -Basecoin actually defines a second transaction type, the `AppTx`, -which enables the functionality to be extended via custom plugins. -To learn more about the `AppTx` and plugin system, see the [plugin design document](plugin-design.md). -To implement your first plugin, see [plugin tutorial](example-plugin.md). diff --git a/docs/guide/basecoin-plugins.md b/docs/guide/basecoin-plugins.md new file mode 100644 index 000000000..cc78d246a --- /dev/null +++ b/docs/guide/basecoin-plugins.md @@ -0,0 +1,162 @@ +# 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`. + +## 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: + +``` +cd $GOPATH/src/github.com/tendermint/basecoin +go install ./docs/guide/src/example-plugin +``` + +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. + +A new blockchain can be initialized and started just like with `basecoin`: + +``` +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: + +``` +example-plugin tx send --to 0x1DA7C74F9C219229FD54CC9F7386D5A3839F0090 --amount 1mycoin +``` + +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: + +``` +example-plugin tx example --amount 1mycoin +example-plugin tx example --amount 1mycoin --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: + +``` +example-plugin query ExamplePlugin.State +``` + +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: + +``` +example-plugin tx example --valid --amount 1mycoin +example-plugin query ExamplePlugin.State +``` + +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! + + +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. + +```golang +type AppTx struct { + Gas int64 `json:"gas"` + Fee Coin `json:"fee"` + Input TxInput `json:"input"` + Name string `json:"type"` // Name of the plugin + Data []byte `json:"data"` // Data for the plugin to process +} +``` + +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. + +## 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 +} +``` + +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! + +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. + +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. + +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`. + +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. + +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. diff --git a/docs/guide/basecoin-tool.md b/docs/guide/basecoin-tool.md index 8af94cdee..52f42d832 100644 --- a/docs/guide/basecoin-tool.md +++ b/docs/guide/basecoin-tool.md @@ -140,3 +140,14 @@ You can reset all blockchain data by running: ``` basecoin unsafe_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. + diff --git a/docs/guide/deployment.md b/docs/guide/deployment.md deleted file mode 100644 index 74e954a25..000000000 --- a/docs/guide/deployment.md +++ /dev/null @@ -1,8 +0,0 @@ -## Deployment - -Up until this point, we have only been testing the code as a blockchain with a single validator node running locally. -This is nice for developing, but it's not a real distributed application yet. - -This section will demonstrate how to launch your basecoin-based application along -with a tendermint testnet and initialize the genesis block for fun and profit. -We do this using the [mintnet-kubernetes tool](https://github.com/tendermint/mintnet-kubernetes). diff --git a/docs/guide/example-plugin.md b/docs/guide/example-plugin.md deleted file mode 100644 index a4592b355..000000000 --- a/docs/guide/example-plugin.md +++ /dev/null @@ -1,389 +0,0 @@ -# Basecoin Example Plugin - -In the [previous tutorial](basecoin-basics.md), -we saw how to start a Basecoin blockchain and use the CLI to send transactions. -Here, we will demonstrate how to extend the blockchain and CLI to support a simple plugin. - -## Overview - -Creating a new plugin and CLI to support it requires a little bit of -boilerplate, but not much. For convenience, we've implemented an extremely -simple example plugin that can be easily modified. The example is under -`docs/guide/src/example-plugin`. To build your own plugin, copy this folder to -a new location and start modifying it there. - -Let's take a look at the files in `docs/guide/src/example-plugin`: - -``` -cmd.go -main.go -plugin.go -``` - -### main.go - -The `main.go` is very simple and does not need to be changed: - -```golang -func main() { - //Initialize example-plugin root command - var RootCmd = &cobra.Command{ - Use: "example-plugin", - Short: "example-plugin usage description", - } - - //Add the default basecoin commands to the root command - RootCmd.AddCommand( - commands.InitCmd, - commands.StartCmd, - commands.TxCmd, - commands.QueryCmd, - commands.KeyCmd, - commands.VerifyCmd, - commands.BlockCmd, - commands.AccountCmd, - commands.UnsafeResetAllCmd, - ) - - //Run the root command - commands.ExecuteWithDebug(RootCmd) -} -``` - -It creates the CLI, exactly like the `basecoin` one. However, if we want our -plugin to be active, we need to make sure it is registered with the -application. In addition, if we want to send transactions to our plugin, we -need to add a new command to the CLI. This is where the `cmd.go` comes in. - -### cmd.go - -First we define the new CLI command and associated flag variables. - -```golang -var ( - //CLI Flags - validFlag bool - - //CLI Plugin Commands - ExamplePluginTxCmd = &cobra.Command{ - Use: "example", - Short: "Create, sign, and broadcast a transaction to the example plugin", - RunE: examplePluginTxCmd, - } -) -``` - -Next within the `init` function we register our plugin's flags and register our -custom plugin command with the root command. This creates a new subcommand -under `tx` (defined below), and ensures the plugin is activated when we start -the app. - -```golang -func init() { - - //Set the Plugin Flags - ExamplePluginTxCmd.Flags().BoolVar(&validFlag, "valid", false, "Set this to make transaction valid") - - //Register a plugin specific CLI command as a subcommand of the tx command - commands.RegisterTxSubcommand(ExamplePluginTxCmd) - - //Register the example with basecoin at start - commands.RegisterStartPlugin("example-plugin", func() types.Plugin { return NewExamplePlugin() }) -} -``` - -We now define the actual function which is called by our CLI command. - -```golang -func examplePluginTxCmd(cmd *cobra.Command, args []string) error { - exampleTx := ExamplePluginTx{validFlag} - exampleTxBytes := wire.BinaryBytes(exampleTx) - return commands.AppTx("example-plugin", exampleTxBytes) -} -``` - -Our function is a simple command with one boolean flag. However, it actually -inherits the persistent flags from the Basecoin framework. These persistent -flags use pointers to these variables stored in `cmd/commands/tx.go`: - -```golang -var ( - //persistent flags - txNodeFlag string - amountFlag string - fromFlag string - seqFlag int - gasFlag int - feeFlag string - chainIDFlag string - - //non-persistent flags - toFlag string - dataFlag string - nameFlag string -) -``` - -If we now compile and run our program, we can see all the options: - -``` -cd $GOPATH/src/github.com/tendermint/basecoin -go install ./docs/guide/src/example-plugin -example-plugin tx example --help -``` - -The output: - -``` -Create, sign, and broadcast a transaction to the example plugin - -Usage: - example-plugin tx example [flags] - -Flags: - --valid Set this to make transaction valid - -Global Flags: - --amount string Coins to send in transaction of the format ,,... (eg: 1btc,2gold,5silver}, - --chain_id string ID of the chain for replay protection (default "test_chain_id") - --fee string Coins for the transaction fee of the format - --from string Path to a private key to sign the transaction (default "key.json") - --gas int The amount of gas for the transaction - --node string Tendermint RPC address (default "tcp://localhost:46657") - --sequence int Sequence number for the account (-1 to autocalculate}, (default -1) -``` - -Cool, eh? - -Before we move on to `plugin.go`, let's look at the `examplePluginTxCmd` -function in `cmd.go`: - -```golang -func examplePluginTxCmd(cmd *cobra.Command, args []string) { - exampleTx := ExamplePluginTx{validFlag} - exampleTxBytes := wire.BinaryBytes(exampleTx) - commands.AppTx("example-plugin", exampleTxBytes) -} -``` - -We read the flag from the CLI library, and then create the example transaction. -Remember that Basecoin itself only knows about two transaction types, `SendTx` -and `AppTx`. All plugin data must be serialized (ie. encoded as a byte-array) -and sent as data in an `AppTx`. The `commands.AppTx` function does this for us -- it creates an `AppTx` with the corresponding data, signs it, and sends it on -to the blockchain. - -### plugin.go - -Ok, now we're ready to actually look at the implementation of the plugin in -`plugin.go`. Note I'll leave out some of the methods as they don't serve any -purpose for this example, but are necessary boilerplate. Your plugin may have -additional requirements that utilize these other methods. Here's what's -relevant for us: - -```golang -type ExamplePluginState struct { - Counter int -} - -type ExamplePluginTx struct { - Valid bool -} - -type ExamplePlugin struct { - name string -} - -func (ep *ExamplePlugin) Name() string { - return ep.name -} - -func (ep *ExamplePlugin) StateKey() []byte { - return []byte("ExamplePlugin.State") -} - -func NewExamplePlugin() *ExamplePlugin { - return &ExamplePlugin{ - name: "example-plugin", - } -} - -func (ep *ExamplePlugin) SetOption(store types.KVStore, key string, value string) (log string) { - return "" -} - -func (ep *ExamplePlugin) RunTx(store types.KVStore, ctx types.CallContext, txBytes []byte) (res abci.Result) { - - // Decode txBytes using go-wire. Attempt to write the txBytes to the variable - // tx, if the txBytes have not been properly encoded from a ExamplePluginTx - // struct wire will produce an error. - var tx ExamplePluginTx - err := wire.ReadBinaryBytes(txBytes, &tx) - if err != nil { - return abci.ErrBaseEncodingError.AppendLog("Error decoding tx: " + err.Error()) - } - - // Perform Transaction Validation - if !tx.Valid { - return abci.ErrInternalError.AppendLog("Valid must be true") - } - - // Load PluginState - var pluginState ExamplePluginState - stateBytes := store.Get(ep.StateKey()) - // If the state does not exist, stateBytes will be initialized - // as an empty byte array with length of zero - if len(stateBytes) > 0 { - err = wire.ReadBinaryBytes(stateBytes, &pluginState) //decode using go-wire - if err != nil { - return abci.ErrInternalError.AppendLog("Error decoding state: " + err.Error()) - } - } - - //App Logic - pluginState.Counter += 1 - - // Save PluginState - store.Set(ep.StateKey(), wire.BinaryBytes(pluginState)) - - return abci.OK -} -``` - -All we're doing here is defining a state and transaction type for our plugin, -and then using the `RunTx` method to define how the transaction updates the -state. Let's break down `RunTx` in parts. First, we deserialize the -transaction: - -```golang -var tx ExamplePluginTx -err := wire.ReadBinaryBytes(txBytes, &tx) -if err != nil { - return abci.ErrBaseEncodingError.AppendLog("Error decoding tx: " + err.Error()) -} -``` - -The transaction is expected to be serialized according to Tendermint's "wire" -format, as defined in the `github.com/tendermint/go-wire` package. If it's not -encoded properly, we return an error. - - -If the transaction deserializes correctly, we can now check if it's valid: - -```golang -if !tx.Valid { - return abci.ErrInternalError.AppendLog("Valid must be true") -} -``` - -The transaction is valid if the `Valid` field is set, otherwise it's not - -simple as that. Finally, we can update the state. In this example, the state -simply counts how many valid transactions we've processed. But the state itself -is serialized and kept in some `store`, which is typically a Merkle tree. So -first we have to load the state from the store and deserialize it: - -```golang -var pluginState ExamplePluginState -stateBytes := store.Get(ep.StateKey()) -if len(stateBytes) > 0 { - err = wire.ReadBinaryBytes(stateBytes, &pluginState) - if err != nil { - return abci.ErrInternalError.AppendLog("Error decoding state: " + err.Error()) - } -} -``` - -Note the state is stored under `ep.StateKey()`, which is defined above as `ExamplePlugin.State`. -Also note, that we do nothing if there is no existing state data. Is that a bug? No, we just make -use of Go's variable initialization, that `pluginState` will contain a `Counter` value of 0. -If your app needs more initialization than empty variables, then do this logic here in an `else` block. - -Finally, we can update the state's `Counter`, and save the state back to the store: - -```golang -//App Logic -pluginState.Counter += 1 - -// Save PluginState -store.Set(ep.StateKey(), wire.BinaryBytes(pluginState)) - -return abci.OK -``` - -And that's it! Now that we have a simple plugin, let's see how to run it. - -## Running your plugin - -First, initialize the new blockchain with - -``` -basecoin init -``` - -If you've already run a basecoin blockchain, reset the data with - -``` -basecoin unsafe_reset_all -``` - -To start the blockchain with your new plugin, simply run - -``` -example-plugin start -``` - -In another window, we can try sending some transactions: - -``` -example-plugin tx send --to 0x1DA7C74F9C219229FD54CC9F7386D5A3839F0090 --amount 100mycoin -``` - -Ok, so that's how we can send a `SendTx` transaction using our `example-plugin` CLI, -but we were already able to do that with the `basecoin` CLI. -With our new CLI, however, we can also send an `ExamplePluginTx`: - -``` -example-plugin tx example --amount 1mycoin -``` - -The transaction is invalid! That's because we didn't specify the `--valid` flag: - -``` -example-plugin tx example --valid --amount 1mycoin -``` - -Tada! We successfuly created, signed, broadcast, and processed our custom transaction type. - -## Query - -Now that we've sent a transaction to update the state, let's query for the state. -Recall that the state is stored under the key `ExamplePlugin.State`: - - -``` -example-plugin query ExamplePlugin.State -``` - -Note the `"value":"0101"` piece. This is the serialized form of the state, -which contains only an integer. -If we send another transaction, and then query again, we'll see the value increment: - -``` -example-plugin tx example --valid --amount 1mycoin -example-plugin query ExamplePlugin.State -``` - -Neat, right? 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 [tutorial on InterBlockchain Communication](ibc.md), -we'll put this proof to work! - -## Next Steps - -In this tutorial we demonstrated how to create a new plugin and how to extend the -basecoin CLI to activate the plugin on the blockchain and to send transactions to it. -Hopefully by now you have some ideas for your own plugin, and feel comfortable implementing them. - -In the [next tutorial](more-examples.md), we tour through some other plugin examples, -addin mple-plugin query ExamplePlugin.Statefeatures for minting new coins, voting, and changing the Tendermint validator set. -But first, you may want to learn a bit more about [the design of the plugin system](plugin-design.md) diff --git a/docs/guide/more-examples.md b/docs/guide/more-examples.md deleted file mode 100644 index 1fc1b545c..000000000 --- a/docs/guide/more-examples.md +++ /dev/null @@ -1,14 +0,0 @@ -# Plugin Examples - -Now that we've seen [how to write a simple plugin](/docs/guide/example-plugin.md) -and taken a look at [how the plugin system is designed](/docs/guide/plugin-design.md), -it's time for some more advanced examples. - -For now, most examples are contained in the `github.com/tendermint/basecoin-examples` repository. -In particular, we have the following: - -1. [Mintcoin](https://github.com/tendermint/basecoin-examples/tree/develop/mintcoin) - a plugin for issuing new Basecoin tokens -2. [Trader](https://github.com/tendermint/basecoin-examples/tree/develop/trader) - a plugin for adding escrow and options features to Basecoin -3. [Stakecoin](https://github.com/tendermint/basecoin-examples/tree/develop/stake) - a plugin for bonding and unbonding Tendermint validators and updating the validator set accordingly -4. [PayToVote](https://github.com/tendermint/basecoin-examples/tree/develop/paytovote) - a plugin for creating issues and voting on them -5. [IBC](/docs/guide/ibc.md) - a plugin for facilitating InterBlockchain Communication diff --git a/docs/guide/plugin-design.md b/docs/guide/plugin-design.md deleted file mode 100644 index e8e7dc92f..000000000 --- a/docs/guide/plugin-design.md +++ /dev/null @@ -1,79 +0,0 @@ -# Basecoin Plugins - -Basecoin implements a simple cryptocurrency, which is useful in and of itself, -but is far more useful if it can support additional functionality. -Here we describe how that functionality can be achieved through a plugin system. - - -## AppTx - -In addition to the `SendTx`, Basecoin also defines another transaction type, the `AppTx`: - -```golang -type AppTx struct { - Gas int64 `json:"gas"` - Fee Coin `json:"fee"` - Input TxInput `json:"input"` - Name string `json:"type"` // Name of the plugin - Data []byte `json:"data"` // Data for the plugin to process -} -``` - -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. - -## 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 -} -``` - -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! - -Any required plugin initialization should be constructed within `SetOption`. -`SetOption` may be called during genesis of basecoin and can be used to set -initial plugin parameters. 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. - -## Examples - -To get started with plugins, see [the example-plugin tutorial](example-plugin.md). -For more examples, see [the advanced plugin tutorial](more-examples.md). -If you're really brave, see the tutorial on [implementing Interblockchain Communication as a plugin](ibc.md).