Merge pull request #1376 from cosmos/bucky/docs-core

docs via example apps
This commit is contained in:
Ethan Buchman 2018-06-29 02:07:06 -04:00 committed by GitHub
commit a88b6b9c97
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 2800 additions and 2134 deletions

View File

@ -9,30 +9,58 @@ NOTE: This documentation is a work-in-progress!
- [Application Architecture](overview/apps.md) - Layers in the application architecture
- [Install](install.md) - Install the library and example applications
- [Core](core)
- [Messages](core/messages.md) - Messages contain the content of a transaction
- [Handlers](core/handlers.md) - Handlers are the workhorse of the app!
- [BaseApp](core/baseapp.md) - BaseApp is the base layer of the application
- [The MultiStore](core/multistore.md) - MultiStore is a rich Merkle database
- [Amino](core/amino.md) - Amino is the primary serialization library used in the SDK
- [Accounts](core/accounts.md) - Accounts are the prototypical object kept in the store
- [Transactions](core/transactions.md) - Transactions wrap messages and provide authentication
- [Keepers](core/keepers.md) - Keepers are the interfaces between handlers
- [Clients](core/clients.md) - Hook up your app to standard CLI and REST
interfaces for clients to use!
- [Advanced](core/advanced.md) - Trigger logic on a timer, use custom
serialization formats, advanced Merkle proofs, and more!
- [Introduction](core/intro.md) - Intro to the tutorial
- [App1 - The Basics](core/app1.md)
- [Messages](core/app1.md#messages) - Messages contain the content of a transaction
- [Stores](core/app1.md#kvstore) - KVStore is a Merkle Key-Value store.
- [Handlers](core/app1.md#handlers) - Handlers are the workhorse of the app!
- [Tx](core/app1.md#tx) - Transactions are the ultimate input to the
application
- [BaseApp](core/app1.md#baseapp) - BaseApp is the base layer of the application
- [App2 - Transactions](core/app2.md)
- [Amino](core/app2.md#amino) - Amino is the primary serialization library used in the SDK
- [Ante Handler](core/app2.md#antehandler) - The AnteHandler
authenticates transactions
- [App3 - Modules: Auth and Bank](core/app3.md)
- [auth.Account](core/app3.md#accounts) - Accounts are the prototypical object kept in the store
- [auth.AccountMapper](core/app3.md#account-mapper) - AccountMapper gets and sets Account on a KVStore
- [auth.StdTx](core/app3.md#stdtx) - `StdTx` is the default implementation of `Tx`
- [auth.StdSignBytes](core/app3.md#signing) - `StdTx` must be signed with certain
information
- [auth.AnteHandler](core/app3.md#antehandler) - The `AnteHandler`
verifies `StdTx`, manages accounts, and deducts fees
- [bank.CoinKeeper](core/app3.md#coinkeeper) - CoinKeeper allows for coin
transfers on an underlying AccountMapper
- [App4 - ABCI](core/app4.md)
- [ABCI](core/app4.md#abci) - ABCI is the interface between Tendermint
and the Cosmos-SDK
- [InitChain](core/app4.md#initchain) - Initialize the application
store
- [BeginBlock](core/app4.md#beginblock) - BeginBlock runs at the
beginning of every block and updates the app about validator behaviour
- [EndBlock](core/app4.md#endblock) - EndBlock runs at the
end of every block and lets the app change the validator set.
- [Query](core/app4.md#query) - Query the application store
- [CheckTx](core/app4.md#checktx) - CheckTx only runs the AnteHandler
- [App5 - Basecoin](core/app5.md) -
- [Directory Structure](core/app5.md#directory-structure) - Keep your
application code organized
- [Tendermint Node](core/app5.md#tendermint-node) - Run a full
blockchain node with your app
- [Clients](core/app5.md#clients) - Hook up your app to CLI and REST
interfaces for clients to use!
- [Modules](modules)
- [Bank](modules/bank.md)
- [Staking](modules/staking.md)
- [Slashing](modules/slashing.md)
- [Provisions](modules/provisions.md)
- [Governance](modules/governance.md)
- [IBC](modules/ibc.md)
- [Bank](modules/README.md#bank)
- [Staking](modules/README.md#stake)
- [Slashing](modules/README.md#slashing)
- [Provisions](modules/README.md#provisions)
- [Governance](modules/README.md#governance)
- [IBC](modules/README.md#ibc)
- [Clients](clients)
- [Running a Node](clients/node.md) - Run a full node!
- [Key Management](clients/keys.md) - Managing user keys
- [CLI](clients/cli.md) - Queries and transactions via command line
- [Light Client Daemon](clients/lcd.md) - Queries and transactions via REST
- [REST Light Client Daemon](clients/rest.md) - Queries and transactions via REST
API

View File

@ -1,289 +0,0 @@
Basecoin Basics
===============
Here we explain how to get started with a basic Basecoin blockchain, how
to send transactions between accounts using the ``basecoin`` tool, and
what is happening under the hood.
Install
-------
With go, it's one command:
::
go get -u github.com/cosmos/cosmos-sdk
If you have trouble, see the `installation guide <./install.html>`__.
TODO: update all the below
Generate some keys
~~~~~~~~~~~~~~~~~~
Let's generate two keys, one to receive an initial allocation of coins,
and one to send some coins to later:
::
basecli keys new cool
basecli keys new friend
You'll need to enter passwords. You can view your key names and
addresses with ``basecli keys list``, or see a particular key's address
with ``basecli keys get <NAME>``.
Initialize Basecoin
-------------------
To initialize a new Basecoin blockchain, run:
::
basecoin init <ADDRESS>
If you prefer not to copy-paste, you can provide the address
programatically:
::
basecoin init $(basecli keys get cool | awk '{print $2}')
This will create the necessary files for a Basecoin blockchain with one
validator and one account (corresponding to your key) in
``~/.basecoin``. For more options on setup, see the `guide to using the
Basecoin tool </docs/guide/basecoin-tool.md>`__.
If you like, you can manually add some more accounts to the blockchain
by generating keys and editing the ``~/.basecoin/genesis.json``.
Start Basecoin
~~~~~~~~~~~~~~
Now we can start Basecoin:
::
basecoin start
You should see blocks start streaming in!
Initialize Light-Client
-----------------------
Now that Basecoin is running we can initialize ``basecli``, the
light-client utility. Basecli is used for sending transactions and
querying the state. Leave Basecoin running and open a new terminal
window. Here run:
::
basecli init --node=tcp://localhost:26657 --genesis=$HOME/.basecoin/genesis.json
If you provide the genesis file to basecli, it can calculate the proper
chainID and validator hash. Basecli needs to get this information from
some trusted source, so all queries done with ``basecli`` can be
cryptographically proven to be correct according to a known validator
set.
Note: that ``--genesis`` only works if there have been no validator set
changes since genesis. If there are validator set changes, you need to
find the current set through some other method.
Send transactions
~~~~~~~~~~~~~~~~~
Now we are ready to send some transactions. First Let's check the
balance of the two accounts we setup earlier:
::
ME=$(basecli keys get cool | awk '{print $2}')
YOU=$(basecli keys get friend | awk '{print $2}')
basecli query account $ME
basecli query account $YOU
The first account is flush with cash, while the second account doesn't
exist. Let's send funds from the first account to the second:
::
basecli tx send --name=cool --amount=1000mycoin --to=$YOU --sequence=1
Now if we check the second account, it should have ``1000`` 'mycoin'
coins!
::
basecli query account $YOU
We can send some of these coins back like so:
::
basecli tx send --name=friend --amount=500mycoin --to=$ME --sequence=1
Note how we use the ``--name`` flag to select a different account to
send from.
If we try to send too much, we'll get an error:
::
basecli tx send --name=friend --amount=500000mycoin --to=$ME --sequence=2
Let's send another transaction:
::
basecli tx send --name=cool --amount=2345mycoin --to=$YOU --sequence=2
Note the ``hash`` value in the response - this is the hash of the
transaction. We can query for the transaction by this hash:
::
basecli query tx <HASH>
See ``basecli tx send --help`` for additional details.
Proof
-----
Even if you don't see it in the UI, the result of every query comes with
a proof. This is a Merkle proof that the result of the query is actually
contained in the state. And the state's Merkle root is contained in a
recent block header. Behind the scenes, ``countercli`` will not only
verify that this state matches the header, but also that the header is
properly signed by the known validator set. It will even update the
validator set as needed, so long as there have not been major changes
and it is secure to do so. So, if you wonder why the query may take a
second... there is a lot of work going on in the background to make sure
even a lying full node can't trick your client.
Accounts and Transactions
-------------------------
For a better understanding of how to further use the tools, it helps to
understand the underlying data structures.
Accounts
~~~~~~~~
The Basecoin state consists entirely of a set of accounts. Each account
contains a 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.
::
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"`
}
If you want to add more coins to a blockchain, you can do so manually in
the ``~/.basecoin/genesis.json`` before you start the blockchain for the
first time.
Accounts are serialized and stored in a Merkle tree under the key
``base/a/<address>``, where ``<address>`` 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/iavl>`__.
Transactions
~~~~~~~~~~~~
Basecoin defines a 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:
::
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 Basecoin, 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.
Clean Up
--------
**WARNING:** Running these commands will wipe out any existing
information in both the ``~/.basecli`` and ``~/.basecoin`` directories,
including private keys.
To remove all the files created and refresh your environment (e.g., if
starting this tutorial again or trying something new), the following
commands are run:
::
basecli reset_all
rm -rf ~/.basecoin
In this guide, we introduced the ``basecoin`` and ``basecli`` tools,
demonstrated how to start a new basecoin blockchain and how to send
tokens between accounts, and discussed the underlying data types for
accounts and transactions, specifically the ``Account`` and the
``SendTx``.

View File

@ -1,215 +0,0 @@
Basecoin Extensions
===================
TODO: re-write for extensions
In the `previous guide <basecoin-basics.md>`__, we saw how to use the
``basecoin`` 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``.
Example Plugin
--------------
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``.
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 in the
`previous guide <basecoin-basics.md>`__:
::
# WARNING: this wipes out data - but counter is only for demos...
rm -rf ~/.counter
countercli reset_all
countercli keys new cool
countercli keys new friend
counter init $(countercli keys get cool | awk '{print $2}')
counter start
The default files are stored in ``~/.counter``. In another window we can
initialize the light-client and send a transaction:
::
countercli init --node=tcp://localhost:26657 --genesis=$HOME/.counter/genesis.json
YOU=$(countercli keys get friend | awk '{print $2}')
countercli tx send --name=cool --amount=1000mycoin --to=$YOU --sequence=1
But the Counter has an additional command, ``countercli tx counter``,
which crafts an ``AppTx`` specifically for this plugin:
::
countercli tx counter --name cool
countercli tx counter --name cool --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:
::
countercli query counter
Tada! We can now see that our custom counter plugin transactions 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. Note that we need the sequence
number here to send the coins (it didn't increment when we just pinged
the counter)
::
countercli tx counter --name cool --countfee=2mycoin --sequence=2 --valid
countercli query counter
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.
Keep it mind that, just like with ``basecli``, the ``countercli``
verifies a proof that the query response is correct and up-to-date.
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.
::
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:
::
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/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``.
The light-client is located in ``cmd/countercli/main.go`` and allows for
transaction and query commands. This file can also be left mostly alone
besides replacing the application name and adding references to new
plugin commands.
Next is the custom commands in ``cmd/countercli/commands/``. These files
are 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).
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.

View File

@ -1,230 +0,0 @@
Glossary
========
This glossary defines many terms used throughout documentation of Quark.
If there is every a concept that seems unclear, check here. This is
mainly to provide a background and general understanding of the
different words and concepts that are used. Other documents will explain
in more detail how to combine these concepts to build a particular
application.
Transaction
-----------
A transaction is a packet of binary data that contains all information
to validate and perform an action on the blockchain. The only other data
that it interacts with is the current state of the chain (key-value
store), and it must have a deterministic action. The transaction is the
main piece of one request.
We currently make heavy use of
`go-amino <https://github.com/tendermint/go-amino>`__ to
provide binary and json encodings and decodings for ``struct`` or
interface\ ``objects. Here, encoding and decoding operations are designed to operate with interfaces nested any amount times (like an onion!). There is one public``\ TxMapper\`
in the basecoin root package, and all modules can register their own
transaction types there. This allows us to deserialize the entire
transaction in one location (even with types defined in other repos), to
easily embed an arbitrary transaction inside another without specifying
the type, and provide an automatic json representation allowing for
users (or apps) to inspect the chain.
Note how we can wrap any other transaction, add a fee level, and not
worry about the encoding in our module any more?
::
type Fee struct {
Fee coin.Coin `json:"fee"`
Payer basecoin.Actor `json:"payer"` // the address who pays the fee
Tx basecoin.Tx `json:"tx"`
}
Context (ctx)
-------------
As a request passes through the system, it may pick up information such
as the block height the request runs at. In order to carry this information
between modules it is saved to the context. Further, all information
must be deterministic from the context in which the request runs (based
on the transaction and the block it was included in) and can be used to
validate the transaction.
Data Store
----------
In order to provide proofs to Tendermint, we keep all data in one
key-value (kv) store which is indexed with a merkle tree. This allows
for the easy generation of a root hash and proofs for queries without
requiring complex logic inside each module. Standardization of this
process also allows powerful light-client tooling as any store data may
be verified on the fly.
The largest limitation of the current implemenation of the kv-store is
that interface that the application must use can only ``Get`` and
``Set`` single data points. That said, there are some data structures
like queues and range queries that are available in ``state`` package.
These provide higher-level functionality in a standard format, but have
not yet been integrated into the kv-store interface.
Isolation
---------
One of the main arguments for blockchain is security. So while we
encourage the use of third-party modules, all developers must be
vigilant against security holes. If you use the
`stack <https://github.com/cosmos/cosmos-sdk/tree/master/stack>`__
package, it will provide two different types of compartmentalization
security.
The first is to limit the working kv-store space of each module. When
``DeliverTx`` is called for a module, it is never given the entire data
store, but rather only its own prefixed subset of the store. This is
achieved by prefixing all keys transparently with
``<module name> + 0x0``, using the null byte as a separator. Since the
module name must be a string, no malicious naming scheme can ever lead
to a collision. Inside a module, we can write using any key value we
desire without the possibility that we have modified data belonging to
separate module.
The second is to add permissions to the transaction context. The
transaction context can specify that the tx has been signed by one or
multiple specific actors.
A transactions will only be executed if the permission requirements have
been fulfilled. For example the sender of funds must have signed, or 2
out of 3 multi-signature actors must have signed a joint account. To
prevent the forgery of account signatures from unintended modules each
permission is associated with the module that granted it (in this case
`auth <https://github.com/cosmos/cosmos-sdk/tree/master/x/auth>`__),
and if a module tries to add a permission for another module, it will
panic. There is also protection if a module creates a brand new fake
context to trick the downstream modules. Each context enforces the rules
on how to make child contexts, and the stack builder enforces
that the context passed from one level to the next is a valid child of
the original one.
These security measures ensure that modules can confidently write to
their local section of the database and trust the permissions associated
with the context, without concern of interference from other modules.
(Okay, if you see a bunch of C-code in the module traversing through all
the memory space of the application, then get worried....)
Handler
-------
The ABCI interface is handled by ``app``, which translates these data
structures into an internal format that is more convenient, but unable
to travel over the wire. The basic interface for any code that modifies
state is the ``Handler`` interface, which provides four methods:
::
Name() string
CheckTx(ctx Context, store state.KVStore, tx Tx) (Result, error)
DeliverTx(ctx Context, store state.KVStore, tx Tx) (Result, error)
SetOption(l log.Logger, store state.KVStore, module, key, value string) (string, error)
Note the ``Context``, ``KVStore``, and ``Tx`` as principal carriers of
information. And that Result is always success, and we have a second
error return for errors (which is much more standard golang that
``res.IsErr()``)
The ``Handler`` interface is designed to be the basis for all modules
that execute transactions, and this can provide a large degree of code
interoperability, much like ``http.Handler`` does in golang web
development.
Modules
-------
TODO: update (s/Modules/handlers+mappers+stores/g) & add Msg + Tx (a signed message)
A module is a set of functionality which should be typically designed as
self-sufficient. Common elements of a module are:
- transaction types (either end transactions, or transaction wrappers)
- custom error codes
- data models (to persist in the kv-store)
- handler (to handle any end transactions)
Dispatcher
----------
We usually will want to have multiple modules working together, and need
to make sure the correct transactions get to the correct module. So we
have ``coin`` sending money, ``roles`` to create multi-sig accounts, and
``ibc`` for following other chains all working together without
interference.
We can then register a ``Dispatcher``, which
also implements the ``Handler`` interface. We then register a list of
modules with the dispatcher. Every module has a unique ``Name()``, which
is used for isolating its state space. We use this same name for routing
transactions. Each transaction implementation must be registed with
go-amino via ``TxMapper``, so we just look at the registered name of this
transaction, which should be of the form ``<module name>/xxx``. The
dispatcher grabs the appropriate module name from the tx name and routes
it if the module is present.
This all seems like a bit of magic, but really we're just making use of
go-amino magic that we are already using, rather than add another layer.
For all the transactions to be properly routed, the only thing you need
to remember is to use the following pattern:
::
const (
NameCoin = "coin"
TypeSend = NameCoin + "/send"
)
Permissions
-----------
TODO: replaces perms with object capabilities/object capability keys
- get rid of IPC
IPC requires a more complex permissioning system to allow the modules to
have limited access to each other and also to allow more types of
permissions than simple public key signatures. Rather than just use an
address to identify who is performing an action, we can use a more
complex structure:
::
type Actor struct {
ChainID string `json:"chain"` // this is empty unless it comes from a different chain
App string `json:"app"` // the app that the actor belongs to
Address data.Bytes `json:"addr"` // arbitrary app-specific unique id
}
Here, the ``Actor`` abstracts any address that can authorize actions,
hold funds, or initiate any sort of transaction. It doesn't just have to
be a pubkey on this chain, it could stem from another app (such as
multi-sig account), or even another chain (via IBC)
``ChainID`` is for IBC, discussed below. Let's focus on ``App`` and
``Address``. For a signature, the App is ``auth``, and any modules can
check to see if a specific public key address signed like this
``ctx.HasPermission(auth.SigPerm(addr))``. However, we can also
authorize a tx with ``roles``, which handles multi-sig accounts, it
checks if there were enough signatures by checking as above, then it can
add the role permission like
``ctx= ctx.WithPermissions(NewPerm(assume.Role))``
In addition to the permissions schema, the Actors are addresses just
like public key addresses. So one can create a mulit-sig role, then send
coin there, which can only be moved upon meeting the authorization
requirements from that module. ``coin`` doesn't even know the existence
of ``roles`` and one could build any other sort of module to provide
permissions (like bind the outcome of an election to move coins or to
modify the accounts on a role).
One idea - not yet implemented - is to provide scopes on the
permissions. Currently, if I sign a transaction to one module, it can
pass it on to any other module over IPC with the same permissions. It
could move coins, vote in an election, or anything else. Ideally, when
signing, one could also specify the scope(s) that this signature
authorizes. The `oauth
protocol <https://api.slack.com/docs/oauth-scopes>`__ also has to deal
with a similar problem, and maybe could provide some inspiration.

View File

@ -1,424 +0,0 @@
IBC
===
TODO: update in light of latest SDK (this document is currently out of date)
One of the most exciting elements of the Cosmos Network is the
InterBlockchain Communication (IBC) protocol, which enables
interoperability across different blockchains. We implemented IBC as a
basecoin plugin, and we'll show you how to use it to send tokens across
blockchains!
Please note: this tutorial assumes familiarity with the Cosmos SDK.
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.
We'll demonstrate exactly how this works below.
Inter BlockChain Communication
------------------------------
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 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)
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 to the latest header available, so long as the
validator set has not changed much. If the validator set is changing,
the client needs to track these 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``. We need to do the following: 1. Register
the details (ie. chain ID and genesis configuration) of ``chain1`` on
``chain2`` 2. Within ``chain1``, broadcast a transaction that creates an
outgoing IBC packet destined for ``chain2`` 3. Broadcast a transaction
to ``chain2`` informing it of the latest state (ie. header and commit
signatures) of ``chain1`` 4. Post the outgoing packet from ``chain1`` to
``chain2``, including the proof that it was indeed committed on
``chain1``. Note ``chain2`` can only verify this proof because it has a
recent header and commit.
Each of these steps involves a separate IBC transaction type. Let's take
them up in turn.
IBCRegisterChainTx
~~~~~~~~~~~~~~~~~~
The ``IBCRegisterChainTx`` is used to register one chain on another. It
contains the chain ID and genesis configuration of the chain to
register:
::
type IBCRegisterChainTx struct { BlockchainGenesis }
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.
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:
::
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.
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.
::
type IBCPacketCreateTx struct {
Packet
}
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.
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:
::
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.
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:
- Genesis configuration
- Latest state
- Headers for recent heights
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.
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.
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``.
Relay
-----
While we need all these packet types internally to keep track of all the
proofs on both chains in a secure manner, for the normal work-flow, we
can run a relay node that handles the cross-chain interaction.
In this case, there are only two steps. First ``basecoin relay init``,
which must be run once to register each chain with the other one, and
make sure they are ready to send and recieve. And then
``basecoin relay start``, which is a long-running process polling the
queue on each side, and relaying all new message to the other block.
This requires that the relay has access to accounts with some funds on
both chains to pay for all the ibc packets it will be forwarding.
Try it out
----------
Now that we have all the background knowledge, let's actually walk
through the tutorial.
Make sure you have installed `basecoin and
basecli </docs/guide/install.md>`__.
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.
If you have any trouble with this, you can also look at the `test
scripts </tests/cli/ibc.sh>`__ or just run ``make test_cli`` in basecoin
repo. Otherwise, open up 5 (yes 5!) terminal tabs....
Preliminaries
~~~~~~~~~~~~~
::
# first, clean up any old garbage for a fresh slate...
rm -rf ~/.ibcdemo/
Let's start by setting up some environment variables and aliases:
::
export BCHOME1_CLIENT=~/.ibcdemo/chain1/client
export BCHOME1_SERVER=~/.ibcdemo/chain1/server
export BCHOME2_CLIENT=~/.ibcdemo/chain2/client
export BCHOME2_SERVER=~/.ibcdemo/chain2/server
alias basecli1="basecli --home $BCHOME1_CLIENT"
alias basecli2="basecli --home $BCHOME2_CLIENT"
alias basecoin1="basecoin --home $BCHOME1_SERVER"
alias basecoin2="basecoin --home $BCHOME2_SERVER"
This will give us some new commands to use instead of raw ``basecli``
and ``basecoin`` to ensure we're using the right configuration for the
chain we want to talk to.
We also want to set some chain IDs:
::
export CHAINID1="test-chain-1"
export CHAINID2="test-chain-2"
And since we will run two different chains on one machine, we need to
maintain different sets of ports:
::
export PORT_PREFIX1=1234
export PORT_PREFIX2=2345
export RPC_PORT1=${PORT_PREFIX1}7
export RPC_PORT2=${PORT_PREFIX2}7
Setup Chain 1
~~~~~~~~~~~~~
Now, let's create some keys that we can use for accounts on
test-chain-1:
::
basecli1 keys new money
basecli1 keys new gotnone
export MONEY=$(basecli1 keys get money | awk '{print $2}')
export GOTNONE=$(basecli1 keys get gotnone | awk '{print $2}')
and create an initial configuration giving lots of coins to the $MONEY
key:
::
basecoin1 init --chain-id $CHAINID1 $MONEY
Now start basecoin:
::
sed -ie "s/4665/$PORT_PREFIX1/" $BCHOME1_SERVER/config.toml
basecoin1 start &> basecoin1.log &
Note the ``sed`` command to replace the ports in the config file. You
can follow the logs with ``tail -f basecoin1.log``
Now we can attach the client to the chain and verify the state. The
first account should have money, the second none:
::
basecli1 init --node=tcp://localhost:${RPC_PORT1} --genesis=${BCHOME1_SERVER}/genesis.json
basecli1 query account $MONEY
basecli1 query account $GOTNONE
Setup Chain 2
~~~~~~~~~~~~~
This is the same as above, except with ``basecli2``, ``basecoin2``, and
``$CHAINID2``. We will also need to change the ports, since we're
running another chain on the same local machine.
Let's create new keys for test-chain-2:
::
basecli2 keys new moremoney
basecli2 keys new broke
MOREMONEY=$(basecli2 keys get moremoney | awk '{print $2}')
BROKE=$(basecli2 keys get broke | awk '{print $2}')
And prepare the genesis block, and start the server:
::
basecoin2 init --chain-id $CHAINID2 $(basecli2 keys get moremoney | awk '{print $2}')
sed -ie "s/4665/$PORT_PREFIX2/" $BCHOME2_SERVER/config.toml
basecoin2 start &> basecoin2.log &
Now attach the client to the chain and verify the state. The first
account should have money, the second none:
::
basecli2 init --node=tcp://localhost:${RPC_PORT2} --genesis=${BCHOME2_SERVER}/genesis.json
basecli2 query account $MOREMONEY
basecli2 query account $BROKE
Connect these chains
~~~~~~~~~~~~~~~~~~~~
OK! So we have two chains running on your local machine, with different
keys on each. Let's hook them up together by starting a relay process to
forward messages from one chain to the other.
The relay account needs some money in it to pay for the ibc messages, so
for now, we have to transfer some cash from the rich accounts before we
start the actual relay.
::
# note that this key.json file is a hardcoded demo for all chains, this will
# be updated in a future release
RELAY_KEY=$BCHOME1_SERVER/key.json
RELAY_ADDR=$(cat $RELAY_KEY | jq .address | tr -d \")
basecli1 tx send --amount=100000mycoin --sequence=1 --to=$RELAY_ADDR--name=money
basecli1 query account $RELAY_ADDR
basecli2 tx send --amount=100000mycoin --sequence=1 --to=$RELAY_ADDR --name=moremoney
basecli2 query account $RELAY_ADDR
Now we can start the relay process.
::
basecoin relay init --chain1-id=$CHAINID1 --chain2-id=$CHAINID2 \
--chain1-addr=tcp://localhost:${RPC_PORT1} --chain2-addr=tcp://localhost:${RPC_PORT2} \
--genesis1=${BCHOME1_SERVER}/genesis.json --genesis2=${BCHOME2_SERVER}/genesis.json \
--from=$RELAY_KEY
basecoin relay start --chain1-id=$CHAINID1 --chain2-id=$CHAINID2 \
--chain1-addr=tcp://localhost:${RPC_PORT1} --chain2-addr=tcp://localhost:${RPC_PORT2} \
--from=$RELAY_KEY &> relay.log &
This should start up the relay, and assuming no error messages came out,
the two chains are now fully connected over IBC. Let's use this to send
our first tx accross the chains...
Sending cross-chain payments
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The hard part is over, we set up two blockchains, a few private keys,
and a secure relay between them. Now we can enjoy the fruits of our
labor...
::
# Here's an empty account on test-chain-2
basecli2 query account $BROKE
::
# Let's send some funds from test-chain-1
basecli1 tx send --amount=12345mycoin --sequence=2 --to=test-chain-2/$BROKE --name=money
::
# give it time to arrive...
sleep 2
# now you should see 12345 coins!
basecli2 query account $BROKE
You're no longer broke! Cool, huh? Now have fun exploring and sending
coins across the chains. And making more accounts as you want to.
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 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!

View File

@ -1,119 +0,0 @@
# Keys CLI
**WARNING: out-of-date and parts are wrong.... please update**
This is as much an example how to expose cobra/viper, as for a cli itself
(I think this code is overkill for what go-keys needs). But please look at
the commands, and give feedback and changes.
`RootCmd` calls some initialization functions (`cobra.OnInitialize` and `RootCmd.PersistentPreRunE`) which serve to connect environmental variables and cobra flags, as well as load the config file. It also validates the flags registered on root and creates the cryptomanager, which will be used by all subcommands.
## Help info
```
# keys help
Keys allows you to manage your local keystore for tendermint.
These keys may be in any format supported by go-crypto and can be
used by light-clients, full nodes, or any other application that
needs to sign with a private key.
Usage:
keys [command]
Available Commands:
get Get details of one key
list List all keys
new Create a new public/private key pair
serve Run the key manager as an http server
update Change the password for a private key
Flags:
--keydir string Directory to store private keys (subdir of root) (default "keys")
-o, --output string Output format (text|json) (default "text")
-r, --root string root directory for config and data (default "/Users/ethan/.tlc")
Use "keys [command] --help" for more information about a command.
```
## Getting the config file
The first step is to load in root, by checking the following in order:
* -r, --root command line flag
* TM_ROOT environmental variable
* default ($HOME/.tlc evaluated at runtime)
Once the `rootDir` is established, the script looks for a config file named `keys.{json,toml,yaml,hcl}` in that directory and parses it. These values will provide defaults for flags of the same name.
There is an example config file for testing out locally, which writes keys to `./.mykeys`. You can
## Getting/Setting variables
When we want to get the value of a user-defined variable (eg. `output`), we can call `viper.GetString("output")`, which will do the following checks, until it finds a match:
* Is `--output` command line flag present?
* Is `TM_OUTPUT` environmental variable set?
* Was a config file found and does it have an `output` variable?
* Is there a default set on the command line flag?
If no variable is set and there was no default, we get back "".
This setup allows us to have powerful command line flags, but use env variables or config files (local or 12-factor style) to avoid passing these arguments every time.
## Nesting structures
Sometimes we don't just need key-value pairs, but actually a multi-level config file, like
```
[mail]
from = "no-reply@example.com"
server = "mail.example.com"
port = 567
password = "XXXXXX"
```
This CLI is too simple to warant such a structure, but I think eg. tendermint could benefit from such an approach. Here are some pointers:
* [Accessing nested keys from config files](https://github.com/spf13/viper#accessing-nested-keys)
* [Overriding nested values with envvars](https://www.netlify.com/blog/2016/09/06/creating-a-microservice-boilerplate-in-go/#nested-config-values) - the mentioned outstanding PR is already merged into master!
* Overriding nested values with cli flags? (use `--log_config.level=info` ??)
I'd love to see an example of this fully worked out in a more complex CLI.
## Have your cake and eat it too
It's easy to render data different ways. Some better for viewing, some better for importing to other programs. You can just add some global (persistent) flags to control the output formatting, and everyone gets what they want.
```
# keys list -e hex
All keys:
betty d0789984492b1674e276b590d56b7ae077f81adc
john b77f4720b220d1411a649b6c7f1151eb6b1c226a
# keys list -e btc
All keys:
betty 3uTF4r29CbtnzsNHZoPSYsE4BDwH
john 3ZGp2Md35iw4XVtRvZDUaAEkCUZP
# keys list -e b64 -o json
[
{
"name": "betty",
"address": "0HiZhEkrFnTidrWQ1Wt64Hf4Gtw=",
"pubkey": {
"type": "secp256k1",
"data": "F83WvhT0KwttSoqQqd_0_r2ztUUaQix5EXdO8AZyREoV31Og780NW59HsqTAb2O4hZ-w-j0Z-4b2IjfdqqfhVQ=="
}
},
{
"name": "john",
"address": "t39HILIg0UEaZJtsfxFR62scImo=",
"pubkey": {
"type": "ed25519",
"data": "t1LFmbg_8UTwj-n1wkqmnTp6NfaOivokEhlYySlGYCY="
}
}
]
```

View File

@ -1,38 +0,0 @@
Replay Protection
-----------------
In order to prevent `replay
attacks <https://en.wikipedia.org/wiki/Replay_attack>`__ a multi account
nonce system has been constructed as a module, which can be found in
``modules/nonce``. By adding the nonce module to the stack, each
transaction is verified for authenticity against replay attacks. This is
achieved by requiring that a new signed copy of the sequence number
which must be exactly 1 greater than the sequence number of the previous
transaction. A distinct sequence number is assigned per chain-id,
application, and group of signers. Each sequence number is tracked as a
nonce-store entry where the key is the marshaled list of actors after
having been sorted by chain, app, and address.
.. code:: golang
// Tx - Nonce transaction structure, contains list of signers and current sequence number
type Tx struct {
Sequence uint32 `json:"sequence"`
Signers []basecoin.Actor `json:"signers"`
Tx basecoin.Tx `json:"tx"`
}
By distinguishing sequence numbers across groups of Signers,
multi-signature Actors need not lock up use of their Address while
waiting for all the members of a multi-sig transaction to occur. Instead
only the multi-sig account will be locked, while other accounts
belonging to that signer can be used and signed with other sequence
numbers.
By abstracting out the nonce module in the stack, entire series of
transactions can occur without needing to verify the nonce for each
member of the series. An common example is a stack which will send coins
and charge a fee. Within the SDK this can be achieved using separate
modules in a stack, one to send the coins and the other to charge the
fee, however both modules do not need to check the nonce. This can occur
as a separate module earlier in the stack.

View File

@ -0,0 +1,402 @@
Using The Staking Module
========================
This project is a demonstration of the Cosmos Hub staking functionality; it is
designed to get validator acquianted with staking concepts and procedures.
Potential validators will be declaring their candidacy, after which users can
delegate and, if they so wish, unbond. This can be practiced using a local or
public testnet.
This example covers initial setup of a two-node testnet between a server in the cloud and a local machine. Begin this tutorial from a cloud machine that you've ``ssh``'d into.
Install
-------
The ``gaiad`` and ``gaiacli`` binaries:
::
go get github.com/cosmos/cosmos-sdk
cd $GOPATH/src/github.com/cosmos/cosmos-sdk
make get_vendor_deps
make install
Let's jump right into it. First, we initialize some default files:
::
gaiad init
which will output:
::
I[03-30|11:20:13.365] Found private validator module=main path=/root/.gaiad/config/priv_validator.json
I[03-30|11:20:13.365] Found genesis file module=main path=/root/.gaiad/config/genesis.json
Secret phrase to access coins:
citizen hungry tennis noise park hire glory exercise link glow dolphin labor design grit apple abandon
This tell us we have a ``priv_validator.json`` and ``genesis.json`` in the ``~/.gaiad/config`` directory. A ``config.toml`` was also created in the same directory. It is a good idea to get familiar with those files. Write down the seed.
The next thing we'll need to is add the key from ``priv_validator.json`` to the ``gaiacli`` key manager. For this we need a seed and a password:
::
gaiacli keys add alice --recover
which will give you three prompts:
::
Enter a passphrase for your key:
Repeat the passphrase:
Enter your recovery seed phrase:
create a password and copy in your seed phrase. The name and address of the key will be output:
::
NAME: ADDRESS: PUBKEY:
alice 67997DD03D527EB439B7193F2B813B05B219CC02 1624DE6220BB89786C1D597050438C728202436552C6226AB67453CDB2A4D2703402FB52B6
You can see all available keys with:
::
gaiacli keys list
Setup Testnet
-------------
Next, we start the daemon (do this in another window):
::
gaiad start
and you'll see blocks start streaming through.
For this example, we're doing the above on a cloud machine. The next steps should be done on your local machine or another server in the cloud, which will join the running testnet then bond/unbond.
Accounts
--------
We have:
- ``alice`` the initial validator (in the cloud)
- ``bob`` receives tokens from ``alice`` then declares candidacy (from local machine)
- ``charlie`` will bond and unbond to ``bob`` (from local machine)
Remember that ``alice`` was already created. On your second machine, install the binaries and create two new keys:
::
gaiacli keys add bob
gaiacli keys add charlie
both of which will prompt you for a password. Now we need to copy the ``genesis.json`` and ``config.toml`` from the first machine (with ``alice``) to the second machine. This is a good time to look at both these files.
The ``genesis.json`` should look something like:
::
{
"app_state": {
"accounts": [
{
"address": "1D9B2356CAADF46D3EE3488E3CCE3028B4283DEE",
"coins": [
{
"denom": "steak",
"amount": 100000
}
]
}
],
"stake": {
"pool": {
"total_supply": 0,
"bonded_shares": {
"num": 0,
"denom": 1
},
"unbonded_shares": {
"num": 0,
"denom": 1
},
"bonded_pool": 0,
"unbonded_pool": 0,
"inflation_last_time": 0,
"inflation": {
"num": 7,
"denom": 100
}
},
"params": {
"inflation_rate_change": {
"num": 13,
"denom": 100
},
"inflation_max": {
"num": 20,
"denom": 100
},
"inflation_min": {
"num": 7,
"denom": 100
},
"goal_bonded": {
"num": 67,
"denom": 100
},
"max_validators": 100,
"bond_denom": "steak"
}
}
},
"validators": [
{
"pub_key": {
"type": "AC26791624DE60",
"value": "rgpc/ctVld6RpSfwN5yxGBF17R1PwMTdhQ9gKVUZp5g="
},
"power": 10,
"name": ""
}
],
"app_hash": "",
"genesis_time": "0001-01-01T00:00:00Z",
"chain_id": "test-chain-Uv1EVU"
}
To notice is that the ``accounts`` field has a an address and a whole bunch of "mycoin". This is ``alice``'s address (todo: dbl check). Under ``validators`` we see the ``pub_key.data`` field, which will match the same field in the ``priv_validator.json`` file.
The ``config.toml`` is long so let's focus on one field:
::
# Comma separated list of seed nodes to connect to
seeds = ""
On the ``alice`` cloud machine, we don't need to do anything here. Instead, we need its IP address. After copying this file (and the ``genesis.json`` to your local machine, you'll want to put the IP in the ``seeds = "138.197.161.74"`` field, in this case, we have a made-up IP. For joining testnets with many nodes, you can add more comma-seperated IPs to the list.
Now that your files are all setup, it's time to join the network. On your local machine, run:
::
gaiad start
and your new node will connect to the running validator (``alice``).
Sending Tokens
--------------
We'll have ``alice`` send some ``mycoin`` to ``bob``, who has now joined the network:
::
gaiacli send --amount=1000mycoin --sequence=0 --name=alice --to=5A35E4CC7B7DC0A5CB49CEA91763213A9AE92AD6 --chain-id=test-chain-Uv1EVU
where the ``--sequence`` flag is to be incremented for each transaction, the ``--name`` flag is the sender (alice), and the ``--to`` flag takes ``bob``'s address. You'll see something like:
::
Please enter passphrase for alice:
{
"check_tx": {
"gas": 30
},
"deliver_tx": {
"tags": [
{
"key": "height",
"value_type": 1,
"value_int": 2963
},
{
"key": "coin.sender",
"value_string": "5D93A6059B6592833CBC8FA3DA90EE0382198985"
},
{
"key": "coin.receiver",
"value_string": "5A35E4CC7B7DC0A5CB49CEA91763213A9AE92AD6"
}
]
},
"hash": "423BD7EA3C4B36AF8AFCCA381C0771F8A698BA77",
"height": 2963
}
TODO: check the above with current actual output.
Check out ``bob``'s account, which should now have 1000 mycoin:
::
gaiacli account 5A35E4CC7B7DC0A5CB49CEA91763213A9AE92AD6
Adding a Second Validator
-------------------------
**This section is wrong/needs to be updated**
Next, let's add the second node as a validator.
First, we need the pub_key data:
** need to make bob a priv_Val above?
::
cat $HOME/.gaia2/priv_validator.json
the first part will look like:
::
{"address":"7B78527942C831E16907F10C3263D5ED933F7E99","pub_key":{"type":"ed25519","data":"96864CE7085B2E342B0F96F2E92B54B18C6CC700186238810D5AA7DFDAFDD3B2"},
and you want the ``pub_key`` ``data`` that starts with ``96864CE``.
Now ``bob`` can create a validator with that pubkey.
::
gaiacli stake create-validator --amount=10mycoin --name=bob --address-validator=<address> --pub-key=<pubkey> --moniker=bobby
with an output like:
::
Please enter passphrase for bob:
{
"check_tx": {
"gas": 30
},
"deliver_tx": {},
"hash": "2A2A61FFBA1D7A59138E0068C82CC830E5103799",
"height": 4075
}
We should see ``bob``'s account balance decrease by 10 mycoin:
::
gaiacli account 5D93A6059B6592833CBC8FA3DA90EE0382198985
To confirm for certain the new validator is active, ask the tendermint node:
::
curl localhost:26657/validators
If you now kill either node, blocks will stop streaming in, because
there aren't enough validators online. Turn it back on and they will
start streaming again.
Now that ``bob`` has declared candidacy, which essentially bonded 10 mycoin and made him a validator, we're going to get ``charlie`` to delegate some coins to ``bob``.
Delegating
----------
First let's have ``alice`` send some coins to ``charlie``:
::
gaiacli send --amount=1000mycoin --sequence=2 --name=alice --to=48F74F48281C89E5E4BE9092F735EA519768E8EF
Then ``charlie`` will delegate some mycoin to ``bob``:
::
gaiacli stake delegate --amount=10mycoin --address-delegator=<charlie's address> --address-validator=<bob's address> --name=charlie
You'll see output like:
::
Please enter passphrase for charlie:
{
"check_tx": {
"gas": 30
},
"deliver_tx": {},
"hash": "C3443BA30FCCC1F6E3A3D6AAAEE885244F8554F0",
"height": 51585
}
And that's it. You can query ``charlie``'s account to see the decrease in mycoin.
To get more information about the candidate, try:
::
gaiacli stake validator <address>
and you'll see output similar to:
::
{
"height": 51899,
"data": {
"pub_key": {
"type": "ed25519",
"data": "52D6FCD8C92A97F7CCB01205ADF310A18411EA8FDCC10E65BF2FCDB05AD1689B"
},
"owner": {
"chain": "",
"app": "sigs",
"addr": "5A35E4CC7B7DC0A5CB49CEA91763213A9AE92AD6"
},
"shares": 20,
"voting_power": 20,
"description": {
"moniker": "bobby",
"identity": "",
"website": "",
"details": ""
}
}
}
It's also possible the query the delegator's bond like so:
::
gaiacli stake delegation --address-delegator=<address> --address-validator=<address>
with an output similar to:
::
{
"height": 325782,
"data": {
"PubKey": {
"type": "ed25519",
"data": "52D6FCD8C92A97F7CCB01205ADF310A18411EA8FDCC10E65BF2FCDB05AD1689B"
},
"Shares": 20
}
}
where the ``--address-delegator`` is ``charlie``'s address and the ``--address-validator`` is ``bob``'s address.
Unbonding
---------
Finally, to relinquish your voting power, unbond some coins. You should see
your VotingPower reduce and your account balance increase.
::
gaiacli stake unbond --amount=5mycoin --name=charlie --address-delegator=<address> --address-validator=<address>
gaiacli account 48F74F48281C89E5E4BE9092F735EA519768E8EF
See the bond decrease with ``gaiacli stake delegation`` like above.

View File

@ -1,204 +0,0 @@
Key Management
==============
Here we explain a bit how to work with your keys, using the
``gaia client keys`` subcommand.
**Note:** This keys tooling is not considered production ready and is
for dev only.
We'll look at what you can do using the six sub-commands of
``gaia client keys``:
::
new
list
get
delete
recover
update
Create keys
-----------
``gaia client keys new`` has two inputs (name, password) and two outputs
(address, seed).
First, we name our key:
::
gaia client keys new alice
This will prompt (10 character minimum) password entry which must be
re-typed. You'll see:
::
Enter a passphrase:
Repeat the passphrase:
alice A159C96AE911F68913E715ED889D211C02EC7D70
**Important** write this seed phrase in a safe place.
It is the only way to recover your account if you ever forget your password.
pelican amateur empower assist awkward claim brave process cliff save album pigeon intact asset
which shows the address of your key named ``alice``, and its recovery
seed. We'll use these shortly.
Adding the ``--output json`` flag to the above command would give this
output:
::
Enter a passphrase:
Repeat the passphrase:
{
"key": {
"name": "alice",
"address": "A159C96AE911F68913E715ED889D211C02EC7D70",
"pubkey": {
"type": "ed25519",
"data": "4BF22554B0F0BF2181187E5E5456E3BF3D96DB4C416A91F07F03A9C36F712B77"
}
},
"seed": "pelican amateur empower assist awkward claim brave process cliff save album pigeon intact asset"
}
To avoid the prompt, it's possible to pipe the password into the
command, e.g.:
::
echo 1234567890 | gaia client keys new fred --output json
After trying each of the three ways to create a key, look at them, use:
::
gaia client keys list
to list all the keys:
::
All keys:
alice 6FEA9C99E2565B44FCC3C539A293A1378CDA7609
bob A159C96AE911F68913E715ED889D211C02EC7D70
charlie 784D623E0C15DE79043C126FA6449B68311339E5
Again, we can use the ``--output json`` flag:
::
[
{
"name": "alice",
"address": "6FEA9C99E2565B44FCC3C539A293A1378CDA7609",
"pubkey": {
"type": "ed25519",
"data": "878B297F1E863CC30CAD71E04A8B3C23DB71C18F449F39E35B954EDB2276D32D"
}
},
{
"name": "bob",
"address": "A159C96AE911F68913E715ED889D211C02EC7D70",
"pubkey": {
"type": "ed25519",
"data": "2127CAAB96C08E3042C5B33C8B5A820079AAE8DD50642DCFCC1E8B74821B2BB9"
}
},
{
"name": "charlie",
"address": "784D623E0C15DE79043C126FA6449B68311339E5",
"pubkey": {
"type": "ed25519",
"data": "4BF22554B0F0BF2181187E5E5456E3BF3D96DB4C416A91F07F03A9C36F712B77"
}
},
]
to get machine readable output.
If we want information about one specific key, then:
::
gaia client keys get charlie --output json
will, for example, return the info for only the "charlie" key returned
from the previous ``gaia client keys list`` command.
The keys tooling can support different types of keys with a flag:
::
gaia client keys new bit --type secp256k1
and you'll see the difference in the ``"type": field from``\ gaia client
keys get\`
Before moving on, let's set an enviroment variable to make
``--output json`` the default.
Either run or put in your ``~/.bash_profile`` the following line:
::
export BC_OUTPUT=json
Recover a key
-------------
Let's say, for whatever reason, you lose a key or forget the password.
On creation, you were given a seed. We'll use it to recover a lost key.
First, let's simulate the loss by deleting a key:
::
gaia client keys delete alice
which prompts for your current password, now rendered obsolete, and
gives a warning message. The only way you can recover your key now is
using the 12 word seed given on initial creation of the key. Let's try
it:
::
gaia client keys recover alice-again
which prompts for a new password then the seed:
::
Enter the new passphrase:
Enter your recovery seed phrase:
strike alien praise vendor term left market practice junior better deputy divert front calm
alice-again CBF5D9CE6DDCC32806162979495D07B851C53451
and voila! You've recovered your key. Note that the seed can be typed
out, pasted in, or piped into the command alongside the password.
To change the password of a key, we can:
::
gaia client keys update alice-again
and follow the prompts.
That covers most features of the keys sub command.
.. raw:: html
<!-- use later in a test script, or more advance tutorial?
SEED=$(echo 1234567890 | gaia client keys new fred -o json | jq .seed | tr -d \")
echo $SEED
(echo qwertyuiop; echo $SEED stamp) | gaia client keys recover oops
(echo qwertyuiop; echo $SEED) | gaia client keys recover derf
gaia client keys get fred -o json
gaia client keys get derf -o json
```
-->

View File

@ -1,83 +0,0 @@
Local Testnet
=============
This tutorial demonstrates the basics of setting up a gaia
testnet locally.
If you haven't already made a key, make one now:
::
gaia client keys new alice
otherwise, use an existing key.
Initialize The Chain
--------------------
Now initialize a gaia chain, using ``alice``'s address:
::
gaia node init 5D93A6059B6592833CBC8FA3DA90EE0382198985 --home=$HOME/.gaia1 --chain-id=gaia-test
This will create all the files necessary to run a single node chain in
``$HOME/.gaia1``: a ``priv_validator.json`` file with the validators
private key, and a ``genesis.json`` file with the list of validators and
accounts.
We'll add a second node on our local machine by initiating a node in a
new directory, with the same address, and copying in the genesis:
::
gaia node init 5D93A6059B6592833CBC8FA3DA90EE0382198985 --home=$HOME/.gaia2 --chain-id=gaia-test
cp $HOME/.gaia1/genesis.json $HOME/.gaia2/genesis.json
We also need to modify ``$HOME/.gaia2/config.toml`` to set new seeds
and ports. It should look like:
::
proxy_app = "tcp://127.0.0.1:26668"
moniker = "anonymous"
fast_sync = true
db_backend = "leveldb"
log_level = "state:info,*:error"
[rpc]
laddr = "tcp://0.0.0.0:26667"
[p2p]
laddr = "tcp://0.0.0.0:26666"
seeds = "0.0.0.0:26656"
Start Nodes
-----------
Now that we've initialized the chains, we can start both nodes:
NOTE: each command below must be started in separate terminal windows. Alternatively, to run this testnet across multiple machines, you'd replace the ``seeds = "0.0.0.0"`` in ``~/.gaia2.config.toml`` with the IP of the first node, and could skip the modifications we made to the config file above because port conflicts would be avoided.
::
gaia node start --home=$HOME/.gaia1
gaia node start --home=$HOME/.gaia2
Now we can initialize a client for the first node, and look up our
account:
::
gaia client init --chain-id=gaia-test --node=tcp://localhost:26657
gaia client query account 5D93A6059B6592833CBC8FA3DA90EE0382198985
To see what tendermint considers the validator set is, use:
::
curl localhost:26657/validators
and compare the information in this file: ``~/.gaia1/priv_validator.json``. The ``address`` and ``pub_key`` fields should match.
To add a second validator on your testnet, you'll need to bond some tokens be declaring candidacy.

View File

@ -0,0 +1,216 @@
//TODO update .rst
# Staking Module
## Overview
The Cosmos Hub is a Tendermint-based Delegated Proof of Stake (DPos) blockchain
system that serves as a backbone of the Cosmos ecosystem. It is operated and
secured by an open and globally decentralized set of validators. Tendermint is
a Byzantine fault-tolerant distributed protocol for consensus among distrusting
parties, in this case the group of validators which produce the blocks for the
Cosmos Hub. To avoid the nothing-at-stake problem, a validator in Tendermint
needs to lock up coins in a bond deposit. Each bond's atoms are illiquid, they
cannot be transferred - in order to become liquid, they must be unbonded, a
process which will take 3 weeks by default at Cosmos Hub launch. Tendermint
protocol messages are signed by the validator's private key and are therefor
attributable. Validators acting outside protocol specifications can be made
accountable through punishing by slashing (burning) their bonded Atoms. On the
other hand, validators are rewarded for their service of securing blockchain
network by the inflationary provisions and transactions fees. This incentivizes
correct behavior of the validators and provides the economic security of the
network.
The native token of the Cosmos Hub is called the Atom; becoming a validator of the
Cosmos Hub requires holding Atoms. However, not all Atom holders are validators
of the Cosmos Hub. More precisely, there is a selection process that determines
the validator set as a subset of all validators (Atom holders that
want to become a validator). The other option for Atom holders is to delegate
their atoms to validators, i.e., being a delegator. A delegator is an Atom
holder that has put its Atoms at stake by delegating it to a validator. By bonding
Atoms to secure the network (and taking a risk of being slashed in case of
misbehaviour), a user is rewarded with inflationary provisions and transaction
fees proportional to the amount of its bonded Atoms. The Cosmos Hub is
designed to efficiently facilitate a small numbers of validators (hundreds),
and large numbers of delegators (tens of thousands). More precisely, it is the
role of the Staking module of the Cosmos Hub to support various staking
functionality including validator set selection, delegating, bonding and
withdrawing Atoms, and the distribution of inflationary provisions and
transaction fees.
## Basic Terms and Definitions
* Cosmsos Hub - a Tendermint-based Delegated Proof of Stake (DPos)
blockchain system
* Atom - native token of the Cosmsos Hub
* Atom holder - an entity that holds some amount of Atoms
* Pool - Global object within the Cosmos Hub which accounts global state
including the total amount of bonded, unbonding, and unbonded atoms
* Validator Share - Share which a validator holds to represent its portion of
bonded, unbonding or unbonded atoms in the pool
* Delegation Share - Shares which a delegation bond holds to represent its
portion of bonded, unbonding or unbonded shares in a validator
* Bond Atoms - a process of locking Atoms in a delegation share which holds them
under protocol control.
* Slash Atoms - the process of burning atoms in the pool and assoiated
validator shares of a misbehaving validator, (not behaving according to the
protocol specification). This process devalues the worth of delegation shares
of the given validator
* Unbond Shares - Process of retrieving atoms from shares. If the shares are
bonded the shares must first remain in an inbetween unbonding state for the
duration of the unbonding period
* Redelegating Shares - Process of redelegating atoms from one validator to
another. This process is instantaneous, but the redelegated atoms are
retrospecively slashable if the old validator is found to misbehave for any
blocks before the redelegation. These atoms are simultaniously slashable
for any new blocks which the new validator misbehavess
* Validator - entity with atoms which is either actively validating the Tendermint
protocol (bonded validator) or vying to validate .
* Bonded Validator - a validator whose atoms are currently bonded and liable to
be slashed. These validators are to be able to sign protocol messages for
Tendermint consensus. At Cosmos Hub genesis there is a maximum of 100
bonded validator positions. Only Bonded Validators receive atom provisions
and fee rewards.
* Delegator - an Atom holder that has bonded Atoms to a validator
* Unbonding period - time required in the unbonding state when unbonding
shares. Time slashable to old validator after a redelegation. Time for which
validators can be slashed after an infraction. To provide the requisite
cryptoeconomic security guarantees, all of these must be equal.
* Atom provisions - The process of increasing the Atom supply. Atoms are
periodically created on the Cosmos Hub and issued to bonded Atom holders.
The goal of inflation is to incentize most of the Atoms in existence to be
bonded. Atoms are distributed unbonded and using the fee_distribution mechanism
* Transaction fees - transaction fee is a fee that is included in a Cosmsos Hub
transaction. The fees are collected by the current validator set and
distributed among validators and delegators in proportion to their bonded
Atom share
* Commission fee - a fee taken from the transaction fees by a validator for
their service
## The pool and the share
At the core of the Staking module is the concept of a pool which denotes a
collection of Atoms contributed by different Atom holders. There are three
pools in the Staking module: the bonded, unbonding, and unbonded pool. Bonded
Atoms are part of the global bonded pool. If a validator or delegator wants to
unbond its shares, these Shares are moved to the the unbonding pool for the
duration of the unbonding period. From here normally Atoms will be moved
directly into the delegators wallet, however under the situation thatn an
entire validator gets unbonded, the Atoms of the delegations will remain with
the validator and moved to the unbonded pool. For each pool, the total amount
of bonded, unbonding, or unbonded Atoms are tracked as well as the current
amount of issued pool-shares, the specific holdings of these shares by
validators are tracked in protocol by the validator object.
A share is a unit of Atom distribution and the value of the share
(share-to-atom exchange rate) can change during system execution. The
share-to-atom exchange rate can be computed as:
`share-to-atom-exchange-rate = size of the pool / ammount of issued shares`
Then for each validator (in a per validator data structure) the protocol keeps
track of the amount of shares the validator owns in a pool. At any point in
time, the exact amount of Atoms a validator has in the pool can be computed as
the number of shares it owns multiplied with the current share-to-atom exchange
rate:
`validator-coins = validator.Shares * share-to-atom-exchange-rate`
The benefit of such accounting of the pool resources is the fact that a
modification to the pool from bonding/unbonding/slashing of Atoms affects only
global data (size of the pool and the number of shares) and not the related
validator data structure, i.e., the data structure of other validators do not
need to be modified. This has the advantage that modifying global data is much
cheaper computationally than modifying data of every validator. Let's explain
this further with several small examples:
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXX TODO make way less verbose lets use bullet points to describe the example
XXX Also need to update to not include bonded atom provisions all atoms are
XXX redistributed with the fee pool now
We consider initially 4 validators p1, p2, p3 and p4, and that each validator
has bonded 10 Atoms to the bonded pool. Furthermore, let's assume that we have
issued initially 40 shares (note that the initial distribution of the shares,
i.e., share-to-atom exchange rate can be set to any meaningful value), i.e.,
share-to-atom-ex-rate = 1 atom per share. Then at the global pool level we
have, the size of the pool is 40 Atoms, and the amount of issued shares is
equal to 40. And for each validator we store in their corresponding data
structure that each has 10 shares of the bonded pool. Now lets assume that the
validator p4 starts process of unbonding of 5 shares. Then the total size of
the pool is decreased and now it will be 35 shares and the amount of Atoms is
35 . Note that the only change in other data structures needed is reducing the
number of shares for a validator p4 from 10 to 5.
Let's consider now the case where a validator p1 wants to bond 15 more atoms to
the pool. Now the size of the pool is 50, and as the exchange rate hasn't
changed (1 share is still worth 1 Atom), we need to create more shares, i.e. we
now have 50 shares in the pool in total. Validators p2, p3 and p4 still have
(correspondingly) 10, 10 and 5 shares each worth of 1 atom per share, so we
don't need to modify anything in their corresponding data structures. But p1
now has 25 shares, so we update the amount of shares owned by p1 in its
data structure. Note that apart from the size of the pool that is in Atoms, all
other data structures refer only to shares.
Finally, let's consider what happens when new Atoms are created and added to
the pool due to inflation. Let's assume that the inflation rate is 10 percent
and that it is applied to the current state of the pool. This means that 5
Atoms are created and added to the pool and that each validator now
proportionally increase it's Atom count. Let's analyse how this change is
reflected in the data structures. First, the size of the pool is increased and
is now 55 atoms. As a share of each validator in the pool hasn't changed, this
means that the total number of shares stay the same (50) and that the amount of
shares of each validator stays the same (correspondingly 25, 10, 10, 5). But
the exchange rate has changed and each share is now worth 55/50 Atoms per
share, so each validator has effectively increased amount of Atoms it has. So
validators now have (correspondingly) 55/2, 55/5, 55/5 and 55/10 Atoms.
The concepts of the pool and its shares is at the core of the accounting in the
Staking module. It is used for managing the global pools (such as bonding and
unbonding pool), but also for distribution of Atoms between validator and its
delegators (we will explain this in section X).
#### Delegator shares
A validator is, depending on its status, contributing Atoms to either the
unbonding or unbonded pool - the validator in turn holds some amount of pool
shares. Not all of a validator's Atoms (and respective shares) are necessarily
owned by the validator, some may be owned by delegators to that validator. The
mechanism for distribution of Atoms (and shares) between a validator and its
delegators is based on a notion of delegator shares. More precisely, every
validator is issuing (local) delegator shares
(`Validator.IssuedDelegatorShares`) that represents some portion of global
shares managed by the validator (`Validator.GlobalStakeShares`). The principle
behind managing delegator shares is the same as described in [Section](#The
pool and the share). We now illustrate it with an example.
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXX TODO make way less verbose lets use bullet points to describe the example
XXX Also need to update to not include bonded atom provisions all atoms are
XXX redistributed with the fee pool now
Let's consider 4 validators p1, p2, p3 and p4, and assume that each validator
has bonded 10 Atoms to the bonded pool. Furthermore, let's assume that we have
issued initially 40 global shares, i.e., that
`share-to-atom-exchange-rate = 1 atom per share`. So we will set
`GlobalState.BondedPool = 40` and `GlobalState.BondedShares = 40` and in the
Validator data structure of each validator `Validator.GlobalStakeShares = 10`.
Furthermore, each validator issued 10 delegator shares which are initially
owned by itself, i.e., `Validator.IssuedDelegatorShares = 10`, where
`delegator-share-to-global-share-ex-rate = 1 global share per delegator share`.
Now lets assume that a delegator d1 delegates 5 atoms to a validator p1 and
consider what are the updates we need to make to the data structures. First,
`GlobalState.BondedPool = 45` and `GlobalState.BondedShares = 45`. Then, for
validator p1 we have `Validator.GlobalStakeShares = 15`, but we also need to
issue also additional delegator shares, i.e.,
`Validator.IssuedDelegatorShares = 15` as the delegator d1 now owns 5 delegator
shares of validator p1, where each delegator share is worth 1 global shares,
i.e, 1 Atom. Lets see now what happens after 5 new Atoms are created due to
inflation. In that case, we only need to update `GlobalState.BondedPool` which
is now equal to 50 Atoms as created Atoms are added to the bonded pool. Note
that the amount of global and delegator shares stay the same but they are now
worth more as share-to-atom-exchange-rate is now worth 50/45 Atoms per share.
Therefore, a delegator d1 now owns:
`delegatorCoins = 5 (delegator shares) * 1 (delegator-share-to-global-share-ex-rate) * 50/45 (share-to-atom-ex-rate) = 5.55 Atoms`

View File

@ -1,64 +0,0 @@
Public Testnets
===============
Here we'll cover the basics of joining a public testnet. These testnets
come and go with various names are we release new versions of tendermint
core. This tutorial covers joining the ``gaia-1`` testnet. To join
other testnets, choose different initialization files, described below.
Get Tokens
----------
If you haven't already `created a key <../key-management.html>`__,
do so now. Copy your key's address and enter it into
`this utility <http://www.cosmosvalidators.com/>`__ which will send you
some ``steak`` testnet tokens.
Get Files
---------
Now, to sync with the testnet, we need the genesis file and seeds. The
easiest way to get them is to clone and navigate to the tendermint
testnet repo:
::
git clone https://github.com/tendermint/testnets ~/testnets
cd ~/testnets/gaia-1/gaia
NOTE: to join a different testnet, change the ``gaia-1/gaia`` filepath
to another directory with testnet inititalization files *and* an
active testnet.
Start Node
----------
Now we can start a new node:it may take awhile to sync with the
existing testnet.
::
gaia node start --home=$HOME/testnets/gaia-1/gaia
Once blocks slow down to about one per second, you're all caught up.
The ``gaia node start`` command will automaticaly generate a validator
private key found in ``~/testnets/gaia-1/gaia/priv_validator.json``.
Finally, let's initialize the gaia client to interact with the testnet:
::
gaia client init --chain-id=gaia-1 --node=tcp://localhost:26657
and check our balance:
::
gaia client query account $MYADDR
Where ``$MYADDR`` is the address originally generated by ``gaia keys new bob``.
You are now ready to declare candidacy or delegate some steaks. See the
`staking module overview <./staking-module.html>`__ for more information
on using the ``gaia client``.

View File

@ -0,0 +1,94 @@
# Testnet Setup
**Note:** This document is incomplete and may not be up-to-date with the
state of the code.
See the [installation guide](../sdk/install.html) for details on
installation.
Here is a quick example to get you off your feet:
First, generate a couple of genesis transactions to be incorporated into
the genesis file, this will create two keys with the password
`1234567890`:
```
gaiad init gen-tx --name=foo --home=$HOME/.gaiad1
gaiad init gen-tx --name=bar --home=$HOME/.gaiad2
gaiacli keys list
```
**Note:** If you've already run these tests you may need to overwrite
keys using the `--owk` flag When you list the keys you should see two
addresses, we'll need these later so take note. Now let's actually
create the genesis files for both nodes:
```
cp -a ~/.gaiad2/config/gentx/. ~/.gaiad1/config/gentx/
cp -a ~/.gaiad1/config/gentx/. ~/.gaiad2/config/gentx/
gaiad init --gen-txs --home=$HOME/.gaiad1 --chain-id=test-chain
gaiad init --gen-txs --home=$HOME/.gaiad2 --chain-id=test-chain
```
**Note:** If you've already run these tests you may need to overwrite
genesis using the `-o` flag. What we just did is copy the genesis
transactions between each of the nodes so there is a common genesis
transaction set; then we created both genesis files independently from
each home directory. Importantly both nodes have independently created
their `genesis.json` and `config.toml` files, which should be identical
between nodes.
Great, now that we've initialized the chains, we can start both nodes in
the background:
```
gaiad start --home=$HOME/.gaiad1 &> gaia1.log &
NODE1_PID=$!
gaia start --home=$HOME/.gaiad2 &> gaia2.log &
NODE2_PID=$!
```
Note that we save the PID so we can later kill the processes. You can
peak at your logs with `tail gaia1.log`, or follow them for a bit with
`tail -f gaia1.log`.
Nice. We can also lookup the validator set:
```
gaiacli validatorset
```
Then, we try to transfer some `steak` to another account:
```
gaiacli account <FOO-ADDR>
gaiacli account <BAR-ADDR>
gaiacli send --amount=10steak --to=<BAR-ADDR> --name=foo --chain-id=test-chain
```
**Note:** We need to be careful with the `chain-id` and `sequence`
Check the balance & sequence with:
```
gaiacli account <BAR-ADDR>
```
To confirm for certain the new validator is active, check tendermint:
```
curl localhost:46657/validators
```
Finally, to relinquish all your power, unbond some coins. You should see
your VotingPower reduce and your account balance increase.
```
gaiacli unbond --chain-id=<chain-id> --name=test
```
That's it!
**Note:** TODO demonstrate edit-candidacy **Note:** TODO demonstrate
delegation **Note:** TODO demonstrate unbond of delegation **Note:**
TODO demonstrate unbond candidate

8
docs/clients/cli.md Normal file
View File

@ -0,0 +1,8 @@
# CLI
See `gaiacli --help` for more details.
Also see the [testnet
tutorial](https://github.com/cosmos/cosmos-sdk/tree/develop/cmd/gaia/testnets).
TODO: cleanup the UX and document this properly.

View File

@ -1,7 +0,0 @@
# Key Management
Here we cover many aspects of handling keys within the Cosmos SDK
framework.
// TODO add relevant key discussion
(related https://github.com/tendermint/tendermint/blob/master/docs/spec/blockchain/encoding.md#public-key-cryptography)

10
docs/clients/keys.md Normal file
View File

@ -0,0 +1,10 @@
# Keys
See the [Tendermint specification](https://github.com/tendermint/tendermint/blob/master/docs/spec/blockchain/encoding.md#public-key-cryptography) for how we work with keys.
See `gaiacli keys --help`.
Also see the [testnet
tutorial](https://github.com/cosmos/cosmos-sdk/tree/develop/cmd/gaia/testnets).
TODO: cleanup the UX and document this properly

10
docs/clients/node.md Normal file
View File

@ -0,0 +1,10 @@
# Running a Node
TODO: document `gaiad`
Options for running the `gaiad` binary are effectively the same as for `tendermint`.
See `gaiad --help` and the
[guide to using Tendermint](https://github.com/tendermint/tendermint/blob/master/docs/using-tendermint.md)
for more details.

6
docs/clients/rest.md Normal file
View File

@ -0,0 +1,6 @@
# REST
See `gaiacli advanced rest-server --help` for more.
Also see the
[work in progress API specification](https://github.com/cosmos/cosmos-sdk/pull/1314)

View File

@ -1,101 +0,0 @@
# Accounts
### auth.Account
```go
// Account is a standard account using a sequence number for replay protection
// and a pubkey for authentication.
type Account interface {
GetAddress() sdk.Address
SetAddress(sdk.Address) error // errors if already set.
GetPubKey() crypto.PubKey // can return nil.
SetPubKey(crypto.PubKey) error
GetAccountNumber() int64
SetAccountNumber(int64) error
GetSequence() int64
SetSequence(int64) error
GetCoins() sdk.Coins
SetCoins(sdk.Coins) error
}
```
Accounts are the standard way for an application to keep track of addresses and their associated balances.
### auth.BaseAccount
```go
// BaseAccount - base account structure.
// Extend this by embedding this in your AppAccount.
// See the examples/basecoin/types/account.go for an example.
type BaseAccount struct {
Address sdk.Address `json:"address"`
Coins sdk.Coins `json:"coins"`
PubKey crypto.PubKey `json:"public_key"`
AccountNumber int64 `json:"account_number"`
Sequence int64 `json:"sequence"`
}
```
The `auth.BaseAccount` struct provides a standard implementation of the Account interface with replay protection.
BaseAccount can be extended by embedding it in your own Account struct.
### auth.AccountMapper
```go
// This AccountMapper encodes/decodes accounts using the
// go-amino (binary) encoding/decoding library.
type AccountMapper struct {
// The (unexposed) key used to access the store from the Context.
key sdk.StoreKey
// The prototypical Account concrete type.
proto Account
// The wire codec for binary encoding/decoding of accounts.
cdc *wire.Codec
}
```
The AccountMapper is responsible for managing and storing the state of all accounts in the application.
Example Initialization:
```go
// File: examples/basecoin/app/app.go
// Define the accountMapper.
app.accountMapper = auth.NewAccountMapper(
cdc,
app.keyAccount, // target store
&types.AppAccount{}, // prototype
)
```
The accountMapper allows you to retrieve the current account state by `GetAccount(ctx Context, addr auth.Address)` and change the state by
`SetAccount(ctx Context, acc Account)`.
Note: To update an account you will first have to get the account, update the appropriate fields with its associated setter method, and then call
`SetAccount(ctx Context, acc updatedAccount)`.
Updating accounts is made easier by using the `Keeper` struct in the `x/bank` module.
Example Initialization:
```go
// File: examples/basecoin/app/app.go
app.coinKeeper = bank.NewKeeper(app.accountMapper)
```
Example Usage:
```go
// Finds account with addr in accountmapper
// Adds coins to account's coin array
// Sets updated account in accountmapper
app.coinKeeper.AddCoins(ctx, addr, coins)
```

View File

@ -1,36 +0,0 @@
# Amino
The SDK is flexible about serialization - application developers can use any
serialization scheme to encode transactions and state. However, the SDK provides
a native serialization format called
[Amino](https://github.com/tendermint/go-amino).
The goal of Amino is to improve over the latest version of Protocol Buffers,
`proto3`. To that end, Amino is compatible with the subset of `proto3` that
excludes the `oneof` keyword.
While `oneof` provides union types, Amino aims to provide interfaces.
The main difference being that with union types, you have to know all the types
up front. But anyone can implement an interface type whenever and however
they like.
To implement interface types, Amino allows any concrete implementation of an
interface to register a globally unique name that is carried along whenever the
type is serialized. This allows Amino to seamlessly deserialize into interface
types!
The primary use for Amino in the SDK is for messages that implement the
`Msg` interface. By registering each message with a distinct name, they are each
given a distinct Amino prefix, allowing them to be easily distinguished in
transactions.
Amino can also be used for persistent storage of interfaces.
To use Amino, simply create a codec, and then register types:
```
cdc := wire.NewCodec()
cdc.RegisterConcrete(MsgSend{}, "cosmos-sdk/Send", nil)
cdc.RegisterConcrete(MsgIssue{}, "cosmos-sdk/Issue", nil)
```

495
docs/core/app1.md Normal file
View File

@ -0,0 +1,495 @@
# The Basics
Here we introduce the basic components of an SDK by building `App1`, a simple bank.
Users have an account address and an account, and they can send coins around.
It has no authentication, and just uses JSON for serialization.
The complete code can be found in [app1.go](examples/app1.go).
## Messages
Messages are the primary inputs to the application state machine.
They define the content of transactions and can contain arbitrary information.
Developers can create messages by implementing the `Msg` interface:
```go
type Msg interface {
// Return the message type.
// Must be alphanumeric or empty.
// Must correspond to name of message handler (XXX).
Type() string
// ValidateBasic does a simple validation check that
// doesn't require access to any other information.
ValidateBasic() error
// Get the canonical byte representation of the Msg.
// This is what is signed.
GetSignBytes() []byte
// Signers returns the addrs of signers that must sign.
// CONTRACT: All signatures must be present to be valid.
// CONTRACT: Returns addrs in some deterministic order.
GetSigners() []Address
}
```
The `Msg` interface allows messages to define basic validity checks, as well as
what needs to be signed and who needs to sign it.
For instance, take the simple token sending message type from app1.go:
```go
// MsgSend to send coins from Input to Output
type MsgSend struct {
From sdk.Address `json:"from"`
To sdk.Address `json:"to"`
Amount sdk.Coins `json:"amount"`
}
// Implements Msg.
func (msg MsgSend) Type() string { return "bank" }
```
It specifies that the message should be JSON marshaled and signed by the sender:
```go
// Implements Msg. JSON encode the message.
func (msg MsgSend) GetSignBytes() []byte {
bz, err := json.Marshal(msg)
if err != nil {
panic(err)
}
return bz
}
// Implements Msg. Return the signer.
func (msg MsgSend) GetSigners() []sdk.Address {
return []sdk.Address{msg.From}
}
```
Note Addresses in the SDK are arbitrary byte arrays that are
[Bech32](https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki) encoded
when displayed as a string or rendered in JSON. Typically, addresses are the hash of
a public key, so we can use them to uniquely identify the required signers for a
transaction.
The basic validity check ensures the From and To address are specified and the
Amount is positive:
```go
// Implements Msg. Ensure the addresses are good and the
// amount is positive.
func (msg MsgSend) ValidateBasic() sdk.Error {
if len(msg.From) == 0 {
return sdk.ErrInvalidAddress("From address is empty")
}
if len(msg.To) == 0 {
return sdk.ErrInvalidAddress("To address is empty")
}
if !msg.Amount.IsPositive() {
return sdk.ErrInvalidCoins("Amount is not positive")
}
return nil
}
```
Note the `ValidateBasic` method is called automatically by the SDK!
## KVStore
The basic persistence layer for an SDK application is the KVStore:
```go
type KVStore interface {
Store
// Get returns nil iff key doesn't exist. Panics on nil key.
Get(key []byte) []byte
// Has checks if a key exists. Panics on nil key.
Has(key []byte) bool
// Set sets the key. Panics on nil key.
Set(key, value []byte)
// Delete deletes the key. Panics on nil key.
Delete(key []byte)
// Iterator over a domain of keys in ascending order. End is exclusive.
// Start must be less than end, or the Iterator is invalid.
// CONTRACT: No writes may happen within a domain while an iterator exists over it.
Iterator(start, end []byte) Iterator
// Iterator over a domain of keys in descending order. End is exclusive.
// Start must be greater than end, or the Iterator is invalid.
// CONTRACT: No writes may happen within a domain while an iterator exists over it.
ReverseIterator(start, end []byte) Iterator
}
```
Note it is unforgiving - it panics on nil keys!
The primary implementation of the KVStore is currently the IAVL store. In the future, we plan to support other Merkle KVStores,
like Ethereum's radix trie.
As we'll soon see, apps have many distinct KVStores, each with a different name and for a different concern.
Access to a store is mediated by *object-capability keys*, which must be granted to a handler during application startup.
## Handlers
Now that we have a message type and a store interface, we can define our state transition function using a handler:
```go
// Handler defines the core of the state transition function of an application.
type Handler func(ctx Context, msg Msg) Result
```
Along with the message, the Handler takes environmental information (a `Context`), and returns a `Result`.
All information necessary for processing a message should be available in the context.
Where is the KVStore in all of this? Access to the KVStore in a message handler is restricted by the Context via object-capability keys.
Only handlers which were given explict access to a store's key will be able to access that store during message processsing.
### Context
The SDK uses a `Context` to propogate common information across functions.
Most importantly, the `Context` restricts access to KVStores based on object-capability keys.
Only handlers which have been given explicit access to a key will be able to access the corresponding store.
For instance, the FooHandler can only load the store it's given the key for:
```go
// newFooHandler returns a Handler that can access a single store.
func newFooHandler(key sdk.StoreKey) sdk.Handler {
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
store := ctx.KVStore(key)
// ...
}
}
```
`Context` is modeled after the Golang
[context.Context](https://golang.org/pkg/context/), which has
become ubiquitous in networking middleware and routing applications as a means
to easily propogate request context through handler functions.
Many methods on SDK objects receive a context as the first argument.
The Context also contains the
[block header](https://github.com/tendermint/tendermint/blob/master/docs/spec/blockchain/blockchain.md#header),
which includes the latest timestamp from the blockchain and other information about the latest block.
See the [Context API
docs](https://godoc.org/github.com/cosmos/cosmos-sdk/types#Context) for more details.
### Result
Handler takes a Context and Msg and returns a Result.
Result is motivated by the corresponding [ABCI result](https://github.com/tendermint/abci/blob/master/types/types.proto#L165).
It contains return values, error information, logs, and meta data about the transaction:
```go
// Result is the union of ResponseDeliverTx and ResponseCheckTx.
type Result struct {
// Code is the response code, is stored back on the chain.
Code ABCICodeType
// Data is any data returned from the app.
Data []byte
// Log is just debug information. NOTE: nondeterministic.
Log string
// GasWanted is the maximum units of work we allow this tx to perform.
GasWanted int64
// GasUsed is the amount of gas actually consumed. NOTE: unimplemented
GasUsed int64
// Tx fee amount and denom.
FeeAmount int64
FeeDenom string
// Tags are used for transaction indexing and pubsub.
Tags Tags
}
```
We'll talk more about these fields later in the tutorial. For now, note that a
`0` value for the `Code` is considered a success, and everything else is a
failure. The `Tags` can contain meta data about the transaction that will allow
us to easily lookup transactions that pertain to particular accounts or actions.
### Handler
Let's define our handler for App1:
```go
// Handle MsgSend.
// NOTE: msg.From, msg.To, and msg.Amount were already validated
// in ValidateBasic().
func handleMsgSend(ctx sdk.Context, key *sdk.KVStoreKey, msg MsgSend) sdk.Result {
// Load the store.
store := ctx.KVStore(key)
// Debit from the sender.
if res := handleFrom(store, msg.From, msg.Amount); !res.IsOK() {
return res
}
// Credit the receiver.
if res := handleTo(store, msg.To, msg.Amount); !res.IsOK() {
return res
}
// Return a success (Code 0).
// Add list of key-value pair descriptors ("tags").
return sdk.Result{
Tags: msg.Tags(),
}
}
```
We have only a single message type, so just one message-specific function to define, `handleMsgSend`.
Note this handler has unrestricted access to the store specified by the capability key `keyAcc`,
so it must define what to store and how to encode it. Later, we'll introduce
higher-level abstractions so Handlers are restricted in what they can do.
For this first example, we use a simple account that is JSON encoded:
```go
type appAccount struct {
Coins sdk.Coins `json:"coins"`
}
```
Coins is a useful type provided by the SDK for multi-asset accounts.
We could just use an integer here for a single coin type, but
it's worth [getting to know
Coins](https://godoc.org/github.com/cosmos/cosmos-sdk/types#Coins).
Now we're ready to handle the two parts of the MsgSend:
```go
func handleFrom(store sdk.KVStore, from sdk.Address, amt sdk.Coins) sdk.Result {
// Get sender account from the store.
accBytes := store.Get(from)
if accBytes == nil {
// Account was not added to store. Return the result of the error.
return sdk.NewError(2, 101, "Account not added to store").Result()
}
// Unmarshal the JSON account bytes.
var acc appAccount
err := json.Unmarshal(accBytes, &acc)
if err != nil {
// InternalError
return sdk.ErrInternal("Error when deserializing account").Result()
}
// Deduct msg amount from sender account.
senderCoins := acc.Coins.Minus(amt)
// If any coin has negative amount, return insufficient coins error.
if !senderCoins.IsNotNegative() {
return sdk.ErrInsufficientCoins("Insufficient coins in account").Result()
}
// Set acc coins to new amount.
acc.Coins = senderCoins
// Encode sender account.
accBytes, err = json.Marshal(acc)
if err != nil {
return sdk.ErrInternal("Account encoding error").Result()
}
// Update store with updated sender account
store.Set(from, accBytes)
return sdk.Result{}
}
func handleTo(store sdk.KVStore, to sdk.Address, amt sdk.Coins) sdk.Result {
// Add msg amount to receiver account
accBytes := store.Get(to)
var acc appAccount
if accBytes == nil {
// Receiver account does not already exist, create a new one.
acc = appAccount{}
} else {
// Receiver account already exists. Retrieve and decode it.
err := json.Unmarshal(accBytes, &acc)
if err != nil {
return sdk.ErrInternal("Account decoding error").Result()
}
}
// Add amount to receiver's old coins
receiverCoins := acc.Coins.Plus(amt)
// Update receiver account
acc.Coins = receiverCoins
// Encode receiver account
accBytes, err := json.Marshal(acc)
if err != nil {
return sdk.ErrInternal("Account encoding error").Result()
}
// Update store with updated receiver account
store.Set(to, accBytes)
return sdk.Result{}
}
```
The handler is straight forward. We first load the KVStore from the context using the granted capability key.
Then we make two state transitions: one for the sender, one for the receiver.
Each one involves JSON unmarshalling the account bytes from the store, mutating
the `Coins`, and JSON marshalling back into the store.
And that's that!
## Tx
The final piece before putting it all together is the `Tx`.
While `Msg` contains the content for particular functionality in the application, the actual input
provided by the user is a serialized `Tx`. Applications may have many implementations of the `Msg` interface,
but they should have only a single implementation of `Tx`:
```go
// Transactions wrap messages.
type Tx interface {
// Gets the Msgs.
GetMsgs() []Msg
}
```
The `Tx` just wraps a `[]Msg`, and may include additional authentication data, like signatures and account nonces.
Applications must specify how their `Tx` is decoded, as this is the ultimate input into the application.
We'll talk more about `Tx` types later, specifically when we introduce the `StdTx`.
In this first application, we won't have any authentication at all. This might
make sense in a private network where access is controlled by alternative means,
like client-side TLS certificates, but in general, we'll want to bake the authentication
right into our state machine. We'll use `Tx` to do that
in the next app. For now, the `Tx` just embeds `MsgSend` and uses JSON:
```go
// Simple tx to wrap the Msg.
type app1Tx struct {
MsgSend
}
// This tx only has one Msg.
func (tx app1Tx) GetMsgs() []sdk.Msg {
return []sdk.Msg{tx.MsgSend}
}
// JSON decode MsgSend.
func txDecoder(txBytes []byte) (sdk.Tx, sdk.Error) {
var tx app1Tx
err := json.Unmarshal(txBytes, &tx)
if err != nil {
return nil, sdk.ErrTxDecode(err.Error())
}
return tx, nil
}
```
## BaseApp
Finally, we stitch it all together using the `BaseApp`.
The BaseApp is an abstraction over the [Tendermint
ABCI](https://github.com/tendermint/abci) that
simplifies application development by handling common low-level concerns.
It serves as the mediator between the two key components of an SDK app: the store
and the message handlers. The BaseApp implements the
[`abci.Application`](https://godoc.org/github.com/tendermint/abci/types#Application) interface.
See the [BaseApp API
documentation](https://godoc.org/github.com/cosmos/cosmos-sdk/baseapp) for more details.
Here is the complete setup for App1:
```go
func NewApp1(logger log.Logger, db dbm.DB) *bapp.BaseApp {
cdc := wire.NewCodec()
// Create the base application object.
app := bapp.NewBaseApp(app1Name, cdc, logger, db)
// Create a capability key for accessing the account store.
keyAccount := sdk.NewKVStoreKey("acc")
// Determine how transactions are decoded.
app.SetTxDecoder(txDecoder)
// Register message routes.
// Note the handler receives the keyAccount and thus
// gets access to the account store.
app.Router().
AddRoute("bank", NewApp1Handler(keyAccount))
// Mount stores and load the latest state.
app.MountStoresIAVL(keyAccount)
err := app.LoadLatestVersion(keyAccount)
if err != nil {
cmn.Exit(err.Error())
}
return app
}
```
Every app will have such a function that defines the setup of the app.
It will typically be contained in an `app.go` file.
We'll talk about how to connect this app object with the CLI, a REST API,
the logger, and the filesystem later in the tutorial. For now, note that this is where we
register handlers for messages and grant them access to stores.
Here, we have only a single Msg type, `bank`, a single store for accounts, and a single handler.
The handler is granted access to the store by giving it the capability key.
In future apps, we'll have multiple stores and handlers, and not every handler will get access to every store.
After setting the transaction decoder and the message handling routes, the final
step is to mount the stores and load the latest version.
Since we only have one store, we only mount one.
## Execution
We're now done the core logic of the app! From here, we can write tests in Go
that initialize the store with accounts and execute transactions by calling
the `app.DeliverTx` method.
In a real setup, the app would run as an ABCI application on top of the
Tendermint consensus engine. It would be initialized by a Genesis file, and it
would be driven by blocks of transactions committed by the underlying Tendermint
consensus. We'll talk more about ABCI and how this all works a bit later, but
feel free to check the
[specification](https://github.com/tendermint/abci/blob/master/specification.md).
We'll also see how to connect our app to a complete suite of components
for running and using a live blockchain application.
For now, we note the follow sequence of events occurs when a transaction is
received (through `app.DeliverTx`):
- serialized transaction is received by `app.DeliverTx`
- transaction is deserialized using `TxDecoder`
- for each message in the transaction, run `msg.ValidateBasic()`
- for each message in the transaction, load the appropriate handler and execute
it with the message
## Conclusion
We now have a complete implementation of a simple app!
In the next section, we'll add another Msg type and another store. Once we have multiple message types
we'll need a better way of decoding transactions, since we'll need to decode
into the `Msg` interface. This is where we introduce Amino, a superior encoding scheme that lets us decode into interface types!

301
docs/core/app2.md Normal file
View File

@ -0,0 +1,301 @@
# Transactions
In the previous app we built a simple `bank` with one message type for sending
coins and one store for storing accounts.
Here we build `App2`, which expands on `App1` by introducing
- a new message type for issuing new coins
- a new store for coin metadata (like who can issue coins)
- a requirement that transactions include valid signatures
Along the way, we'll be introduced to Amino for encoding and decoding
transactions and to the AnteHandler for processing them.
The complete code can be found in [app2.go](examples/app2.go).
## Message
Let's introduce a new message type for issuing coins:
```go
// MsgIssue to allow a registered issuer
// to issue new coins.
type MsgIssue struct {
Issuer sdk.Address
Receiver sdk.Address
Coin sdk.Coin
}
// Implements Msg.
func (msg MsgIssue) Type() string { return "issue" }
```
Note the `Type()` method returns `"issue"`, so this message is of a different
type and will be executed by a different handler than `MsgSend`. The other
methods for `MsgIssue` are similar to `MsgSend`.
## Handler
We'll need a new handler to support the new message type. It just checks if the
sender of the `MsgIssue` is the correct issuer for the given coin type, as per the information
in the issuer store:
```go
// Handle MsgIssue
func handleMsgIssue(keyIssue *sdk.KVStoreKey, keyAcc *sdk.KVStoreKey) sdk.Handler {
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
issueMsg, ok := msg.(MsgIssue)
if !ok {
return sdk.NewError(2, 1, "MsgIssue is malformed").Result()
}
// Retrieve stores
issueStore := ctx.KVStore(keyIssue)
accStore := ctx.KVStore(keyAcc)
// Handle updating coin info
if res := handleIssuer(issueStore, issueMsg.Issuer, issueMsg.Coin); !res.IsOK() {
return res
}
// Issue coins to receiver using previously defined handleTo function
if res := handleTo(accStore, issueMsg.Receiver, []sdk.Coin{issueMsg.Coin}); !res.IsOK() {
return res
}
return sdk.Result{
// Return result with Issue msg tags
Tags: issueMsg.Tags(),
}
}
}
func handleIssuer(store sdk.KVStore, issuer sdk.Address, coin sdk.Coin) sdk.Result {
// the issuer address is stored directly under the coin denomination
denom := []byte(coin.Denom)
infoBytes := store.Get(denom)
if infoBytes == nil {
return sdk.ErrInvalidCoins(fmt.Sprintf("Unknown coin type %s", coin.Denom)).Result()
}
var coinInfo coinInfo
err := json.Unmarshal(infoBytes, &coinInfo)
if err != nil {
return sdk.ErrInternal("Error when deserializing coinInfo").Result()
}
// Msg Issuer is not authorized to issue these coins
if !bytes.Equal(coinInfo.Issuer, issuer) {
return sdk.ErrUnauthorized(fmt.Sprintf("Msg Issuer cannot issue tokens: %s", coin.Denom)).Result()
}
return sdk.Result{}
}
// coinInfo stores meta data about a coin
type coinInfo struct {
Issuer sdk.Address `json:"issuer"`
}
```
Note we've introduced the `coinInfo` type to store the issuer address for each coin.
We JSON serialize this type and store it directly under the denomination in the
issuer store. We could of course add more fields and logic around this,
like including the current supply of coins in existence, and enforcing a maximum supply,
but that's left as an excercise for the reader :).
## Amino
Now that we have two implementations of `Msg`, we won't know before hand
which type is contained in a serialized `Tx`. Ideally, we would use the
`Msg` interface inside our `Tx` implementation, but the JSON decoder can't
decode into interface types. In fact, there's no standard way to unmarshal
into interfaces in Go. This is one of the primary reasons we built
[Amino](https://github.com/tendermint/go-amino) :).
While SDK developers can encode transactions and state objects however they
like, Amino is the recommended format. The goal of Amino is to improve over the latest version of Protocol Buffers,
`proto3`. To that end, Amino is compatible with the subset of `proto3` that
excludes the `oneof` keyword.
While `oneof` provides union types, Amino aims to provide interfaces.
The main difference being that with union types, you have to know all the types
up front. But anyone can implement an interface type whenever and however
they like.
To implement interface types, Amino allows any concrete implementation of an
interface to register a globally unique name that is carried along whenever the
type is serialized. This allows Amino to seamlessly deserialize into interface
types!
The primary use for Amino in the SDK is for messages that implement the
`Msg` interface. By registering each message with a distinct name, they are each
given a distinct Amino prefix, allowing them to be easily distinguished in
transactions.
Amino can also be used for persistent storage of interfaces.
To use Amino, simply create a codec, and then register types:
```
func NewCodec() *wire.Codec {
cdc := wire.NewCodec()
cdc.RegisterInterface((*sdk.Msg)(nil), nil)
cdc.RegisterConcrete(MsgSend{}, "example/MsgSend", nil)
cdc.RegisterConcrete(MsgIssue{}, "example/MsgIssue", nil)
return cdc
}
```
Amino supports encoding and decoding in both a binary and JSON format.
See the [codec API docs](https://godoc.org/github.com/tendermint/go-amino#Codec) for more details.
## Tx
Now that we're using Amino, we can embed the `Msg` interface directly in our
`Tx`. We can also add a public key and a signature for authentication.
```go
// Simple tx to wrap the Msg.
type app2Tx struct {
sdk.Msg
PubKey crypto.PubKey
Signature crypto.Signature
}
// This tx only has one Msg.
func (tx app2Tx) GetMsgs() []sdk.Msg {
return []sdk.Msg{tx.Msg}
}
```
We don't need a custom TxDecoder function anymore, since we're just using the
Amino codec!
## AnteHandler
Now that we have an implementation of `Tx` that includes more than just the Msg,
we need to specify how that extra information is validated and processed. This
is the role of the `AnteHandler`. The word `ante` here denotes "before", as the
`AnteHandler` is run before a `Handler`. While an app can have many Handlers,
one for each set of messages, it can have only a single `AnteHandler` that
corresponds to its single implementation of `Tx`.
The AnteHandler resembles a Handler:
```go
type AnteHandler func(ctx Context, tx Tx) (newCtx Context, result Result, abort bool)
```
Like Handler, AnteHandler takes a Context that restricts its access to stores
according to whatever capability keys it was granted. Instead of a `Msg`,
however, it takes a `Tx`.
Like Handler, AnteHandler returns a `Result` type, but it also returns a new
`Context` and an `abort bool`.
For `App2`, we simply check if the PubKey matches the Address, and the Signature validates with the PubKey:
```go
// Simple anteHandler that ensures msg signers have signed.
// Provides no replay protection.
func antehandler(ctx sdk.Context, tx sdk.Tx) (_ sdk.Context, _ sdk.Result, abort bool) {
appTx, ok := tx.(app2Tx)
if !ok {
// set abort boolean to true so that we don't continue to process failed tx
return ctx, sdk.ErrTxDecode("Tx must be of format app2Tx").Result(), true
}
// expect only one msg in app2Tx
msg := tx.GetMsgs()[0]
signerAddrs := msg.GetSigners()
if len(signerAddrs) != len(appTx.GetSignatures()) {
return ctx, sdk.ErrUnauthorized("Number of signatures do not match required amount").Result(), true
}
signBytes := msg.GetSignBytes()
for i, addr := range signerAddrs {
sig := appTx.GetSignatures()[i]
// check that submitted pubkey belongs to required address
if !bytes.Equal(sig.PubKey.Address(), addr) {
return ctx, sdk.ErrUnauthorized("Provided Pubkey does not match required address").Result(), true
}
// check that signature is over expected signBytes
if !sig.PubKey.VerifyBytes(signBytes, sig.Signature) {
return ctx, sdk.ErrUnauthorized("Signature verification failed").Result(), true
}
}
// authentication passed, app to continue processing by sending msg to handler
return ctx, sdk.Result{}, false
}
```
## App2
Let's put it all together now to get App2:
```go
func NewApp2(logger log.Logger, db dbm.DB) *bapp.BaseApp {
cdc := NewCodec()
// Create the base application object.
app := bapp.NewBaseApp(app2Name, cdc, logger, db)
// Create a key for accessing the account store.
keyAccount := sdk.NewKVStoreKey("acc")
// Create a key for accessing the issue store.
keyIssue := sdk.NewKVStoreKey("issue")
// set antehandler function
app.SetAnteHandler(antehandler)
// Register message routes.
// Note the handler gets access to the account store.
app.Router().
AddRoute("send", handleMsgSend(keyAccount)).
AddRoute("issue", handleMsgIssue(keyAccount, keyIssue))
// Mount stores and load the latest state.
app.MountStoresIAVL(keyAccount, keyIssue)
err := app.LoadLatestVersion(keyAccount)
if err != nil {
cmn.Exit(err.Error())
}
return app
}
```
The main difference here, compared to `App1`, is that we use a second capability
key for a second store that is *only* passed to a second handler, the
`handleMsgIssue`. The first `handleMsgSend` has no access to this second store and cannot read or write to
it, ensuring a strong separation of concerns.
Note also that we do not need to use `SetTxDecoder` here - now that we're using
Amino, we simply create a codec, register our types on the codec, and pass the
codec into `NewBaseApp`. The SDK takes care of the rest for us!
## Conclusion
We've expanded on our first app by adding a new message type for issuing coins,
and by checking signatures. We learned how to use Amino for decoding into
interface types, allowing us to support multiple Msg types, and we learned how
to use the AnteHandler to validate transactions.
Unfortunately, our application is still insecure, because any valid transaction
can be replayed multiple times to drain someones account! Besides, validating
signatures and preventing replays aren't things developers should have to think
about.
In the next section, we introduce the built-in SDK modules `auth` and `bank`,
which respectively provide secure implementations for all our transaction authentication
and coin transfering needs.

370
docs/core/app3.md Normal file
View File

@ -0,0 +1,370 @@
# Modules
In the previous app, we introduced a new `Msg` type and used Amino to encode
transactions. We also introduced additional data to the `Tx`, and used a simple
`AnteHandler` to validate it.
Here, in `App3`, we introduce two built-in SDK modules to
replace the `Msg`, `Tx`, `Handler`, and `AnteHandler` implementations we've seen
so far: `x/auth` and `x/bank`.
The `x/auth` module implements `Tx` and `AnteHandler` - it has everything we need to
authenticate transactions. It also includes a new `Account` type that simplifies
working with accounts in the store.
The `x/bank` module implements `Msg` and `Handler` - it has everything we need
to transfer coins between accounts.
Here, we'll introduce the important types from `x/auth` and `x/bank`, and use
them to build `App3`, our shortest app yet. The complete code can be found in
[app3.go](examples/app3.go), and at the end of this section.
For more details, see the
[x/auth](https://godoc.org/github.com/cosmos/cosmos-sdk/x/auth) and
[x/bank](https://godoc.org/github.com/cosmos/cosmos-sdk/x/bank) API documentation.
## Accounts
The `x/auth` module defines a model of accounts much like Ethereum.
In this model, an account contains:
- Address for identification
- PubKey for authentication
- AccountNumber to prune empty accounts
- Sequence to prevent transaction replays
- Coins to carry a balance
Note that the `AccountNumber` is a unique number that is assigned when the account is
created, and the `Sequence` is incremented by one every time a transaction is
sent from the account.
### Account
The `Account` interface captures this account model with getters and setters:
```go
// Account is a standard account using a sequence number for replay protection
// and a pubkey for authentication.
type Account interface {
GetAddress() sdk.Address
SetAddress(sdk.Address) error // errors if already set.
GetPubKey() crypto.PubKey // can return nil.
SetPubKey(crypto.PubKey) error
GetAccountNumber() int64
SetAccountNumber(int64) error
GetSequence() int64
SetSequence(int64) error
GetCoins() sdk.Coins
SetCoins(sdk.Coins) error
}
```
Note this is a low-level interface - it allows any of the fields to be over
written. As we'll soon see, access can be restricted using the `Keeper`
paradigm.
### BaseAccount
The default implementation of `Account` is the `BaseAccount`:
```go
// BaseAccount - base account structure.
// Extend this by embedding this in your AppAccount.
// See the examples/basecoin/types/account.go for an example.
type BaseAccount struct {
Address sdk.Address `json:"address"`
Coins sdk.Coins `json:"coins"`
PubKey crypto.PubKey `json:"public_key"`
AccountNumber int64 `json:"account_number"`
Sequence int64 `json:"sequence"`
}
```
It simply contains a field for each of the methods.
### AccountMapper
In previous apps using our `appAccount`, we handled
marshaling/unmarshaling the account from the store ourselves, by performing
operations directly on the KVStore. But unrestricted access to a KVStore isn't really the interface we want
to work with in our applications. In the SDK, we use the term `Mapper` to refer
to an abstaction over a KVStore that handles marshalling and unmarshalling a
particular data type to and from the underlying store.
The `x/auth` module provides an `AccountMapper` that allows us to get and
set `Account` types to the store. Note the benefit of using the `Account`
interface here - developers can implement their own account type that extends
the `BaseAccount` to store additional data without requiring another lookup from
the store.
Creating an AccountMapper is easy - we just need to specify a codec, a
capability key, and a prototype of the object being encoded
```go
accountMapper := auth.NewAccountMapper(cdc, keyAccount, &auth.BaseAccount{})
```
Then we can get, modify, and set accounts. For instance, we could double the
amount of coins in an account:
```go
acc := accountMapper.GetAccount(ctx, addr)
acc.SetCoins(acc.Coins.Plus(acc.Coins))
accountMapper.SetAccount(ctx, addr)
```
Note that the `AccountMapper` takes a `Context` as the first argument, and will
load the KVStore from there using the capability key it was granted on creation.
Also note that you must explicitly call `SetAccount` after mutating an account
for the change to persist!
See the [AccountMapper API
docs](https://godoc.org/github.com/cosmos/cosmos-sdk/x/auth#AccountMapper) for more information.
## StdTx
Now that we have a native model for accounts, it's time to introduce the native
`Tx` type, the `auth.StdTx`:
```go
// StdTx is a standard way to wrap a Msg with Fee and Signatures.
// NOTE: the first signature is the FeePayer (Signatures must not be nil).
type StdTx struct {
Msgs []sdk.Msg `json:"msg"`
Fee StdFee `json:"fee"`
Signatures []StdSignature `json:"signatures"`
Memo string `json:"memo"`
}
```
This is the standard form for a transaction in the SDK. Besides the Msgs, it
includes:
- a fee to be paid by the first signer
- replay protecting nonces in the signature
- a memo of prunable additional data
Details on how these components are validated is provided under
[auth.AnteHandler](#antehandler) below.
The standard form for signatures is `StdSignature`:
```go
// StdSignature wraps the Signature and includes counters for replay protection.
// It also includes an optional public key, which must be provided at least in
// the first transaction made by the account.
type StdSignature struct {
crypto.PubKey `json:"pub_key"` // optional
crypto.Signature `json:"signature"`
AccountNumber int64 `json:"account_number"`
Sequence int64 `json:"sequence"`
}
```
The signature includes both an `AccountNumber` and a `Sequence`.
The `Sequence` must match the one in the
corresponding account when the transaction is processed, and will increment by
one with every transaction. This prevents the same
transaction from being replayed multiple times, resolving the insecurity that
remains in App2.
The `AccountNumber` is also for replay protection - it allows accounts to be
deleted from the store when they run out of accounts. If an account receives
coins after it is deleted, the account will be re-created, with the Sequence
reset to 0, but a new AccountNumber. If it weren't for the AccountNumber, the
last sequence of transactions made by the account before it was deleted could be
replayed!
Finally, the standard form for a transaction fee is `StdFee`:
```go
// StdFee includes the amount of coins paid in fees and the maximum
// gas to be used by the transaction. The ratio yields an effective "gasprice",
// which must be above some miminum to be accepted into the mempool.
type StdFee struct {
Amount sdk.Coins `json:"amount"`
Gas int64 `json:"gas"`
}
```
The fee must be paid by the first signer. This allows us to quickly check if the
transaction fee can be paid, and reject the transaction if not.
## Signing
The `StdTx` supports multiple messages and multiple signers.
To sign the transaction, each signer must collect the following information:
- the ChainID
- the AccountNumber and Sequence for the given signer's account (from the
blockchain)
- the transaction fee
- the list of transaction messages
- an optional memo
Then they can compute the transaction bytes to sign using the
`auth.StdSignBytes` function:
```go
bytesToSign := StdSignBytes(chainID, accNum, accSequence, fee, msgs, memo)
```
Note these bytes are unique for each signer, as they depend on the particular
signers AccountNumber, Sequence, and optional memo. To facilitate easy
inspection before signing, the bytes are actually just a JSON encoded form of
all the relevant information.
## AnteHandler
As we saw in `App2`, we can use an `AnteHandler` to authenticate transactions
before we handle any of their internal messages. While previously we implemented
our own simple `AnteHandler`, the `x/auth` module provides a much more advanced
one that uses `AccountMapper` and works with `StdTx`:
```go
app.SetAnteHandler(auth.NewAnteHandler(accountMapper, feeKeeper))
```
The AnteHandler provided by `x/auth` enforces the following rules:
- the memo must not be too big
- the right number of signatures must be provided (one for each unique signer
returned by `msg.GetSigner` for each `msg`)
- any account signing for the first-time must include a public key in the
StdSignature
- the signatures must be valid when authenticated in the same order as specified
by the messages
Note that validating
signatures requires checking that the correct account number and sequence was
used by each signer, as this information is required in the `StdSignBytes`.
If any of the above are not satisfied, the AnteHandelr returns an error.
If all of the above verifications pass, the AnteHandler makes the following
changes to the state:
- increment account sequence by one for all signers
- set the pubkey in the account for any first-time signers
- deduct the fee from the first signer's account
Recall that incrementing the `Sequence` prevents "replay attacks" where
the same message could be executed over and over again.
The PubKey is required for signature verification, but it is only required in
the StdSignature once. From that point on, it will be stored in the account.
The fee is paid by the first address returned by `msg.GetSigners()` for the first `Msg`,
as provided by the `FeePayer(tx Tx) sdk.Address` function.
## CoinKeeper
Now that we've seen the `auth.AccountMapper` and how its used to build a
complete AnteHandler, it's time to look at how to build higher-level
abstractions for taking action on accounts.
Earlier, we noted that `Mappers` are abstactions over KVStores that handle
marshalling and unmarshalling data types to and from underlying stores.
We can build another abstraction on top of `Mappers` that we call `Keepers`,
which expose only limitted functionality on the underlying types stored by the `Mapper`.
For instance, the `x/bank` module defines the canonical versions of `MsgSend`
and `MsgIssue` for the SDK, as well as a `Handler` for processing them. However,
rather than passing a `KVStore` or even an `AccountMapper` directly to the handler,
we introduce a `bank.Keeper`, which can only be used to transfer coins in and out of accounts.
This allows us to determine up front that the only effect the bank module's
`Handler` can have on the store is to change the amount of coins in an account -
it can't increment sequence numbers, change PubKeys, or otherwise.
A `bank.Keeper` is easily instantiated from an `AccountMapper`:
```go
coinKeeper = bank.NewKeeper(accountMapper)
```
We can then use it within a handler, instead of working directly with the
`AccountMapper`. For instance, to add coins to an account:
```go
// Finds account with addr in AccountMapper.
// Adds coins to account's coin array.
// Sets updated account in AccountMapper
app.coinKeeper.AddCoins(ctx, addr, coins)
```
See the [bank.Keeper API
docs](https://godoc.org/github.com/cosmos/cosmos-sdk/x/bank#Keeper) for the full set of methods.
Note we can refine the `bank.Keeper` by restricting it's method set. For
instance, the
[bank.ViewKeeper](https://godoc.org/github.com/cosmos/cosmos-sdk/x/bank#ViewKeeper)
is a read-only version, while the
[bank.SendKeeper](https://godoc.org/github.com/cosmos/cosmos-sdk/x/bank#SendKeeper)
only executes transfers of coins from input accounts to output
accounts.
We use this `Keeper` paradigm extensively in the SDK as the way to define what
kind of functionality each module gets access to. In particular, we try to
follow the *principle of least authority*.
Rather than providing full blown access to the `KVStore` or the `AccountMapper`,
we restrict access to a small number of functions that do very specific things.
## App3
With the `auth.AccountMapper` and `bank.Keeper` in hand,
we're now ready to build `App3`.
The `x/auth` and `x/bank` modules do all the heavy lifting:
```go
func NewApp3(logger log.Logger, db dbm.DB) *bapp.BaseApp {
// Create the codec with registered Msg types
cdc := NewCodec()
// Create the base application object.
app := bapp.NewBaseApp(app3Name, cdc, logger, db)
// Create a key for accessing the account store.
keyAccount := sdk.NewKVStoreKey("acc")
keyFees := sdk.NewKVStoreKey("fee") // TODO
// Set various mappers/keepers to interact easily with underlying stores
accountMapper := auth.NewAccountMapper(cdc, keyAccount, &auth.BaseAccount{})
coinKeeper := bank.NewKeeper(accountMapper)
feeKeeper := auth.NewFeeCollectionKeeper(cdc, keyFees)
app.SetAnteHandler(auth.NewAnteHandler(accountMapper, feeKeeper))
// Register message routes.
// Note the handler gets access to
app.Router().
AddRoute("send", bank.NewHandler(coinKeeper))
// Mount stores and load the latest state.
app.MountStoresIAVL(keyAccount, keyFees)
err := app.LoadLatestVersion(keyAccount)
if err != nil {
cmn.Exit(err.Error())
}
return app
}
```
Note we use `bank.NewHandler`, which handles only `bank.MsgSend`,
and receives only the `bank.Keeper`. See the
[x/bank API docs](https://godoc.org/github.com/cosmos/cosmos-sdk/x/bank)
for more details.
## Conclusion
Armed with native modules for authentication and coin transfer,
emboldened by the paradigm of mappers and keepers,
and ever invigorated by the desire to build secure state-machines,
we find ourselves here with a full-blown, all-checks-in-place, multi-asset
cryptocurrency - the beating heart of the Cosmos-SDK.

73
docs/core/app4.md Normal file
View File

@ -0,0 +1,73 @@
# ABCI
The Application BlockChain Interface, or ABCI, is a powerfully
delineated boundary between the Cosmos-SDK and Tendermint.
It separates the logical state transition machine of your application from
its secure replication across many physical machines.
By providing a clear, language agnostic boundary between applications and consensus,
ABCI provides tremendous developer flexibility and [support in many
languages](https://tendermint.com/ecosystem). That said, it is still quite a low-level protocol, and
requires frameworks to be built to abstract over that low-level componentry.
The Cosmos-SDK is one such framework.
While we've already seen `DeliverTx`, the workhorse of any ABCI application,
here we will introduce the other ABCI requests sent by Tendermint, and
how we can use them to build more advanced applications. For a more complete
depiction of the ABCI and how its used, see
[the
specification](https://github.com/tendermint/abci/blob/master/specification.md)
## InitChain
In our previous apps, we built out all the core logic, but we never specified
how the store should be initialized. For that, we use the `app.InitChain` method,
which is called once by Tendermint the very first time the application boots up.
The InitChain request contains a variety of Tendermint information, like the consensus
parameters and an initial validator set, but it also contains an opaque blob of
application specific bytes - typically JSON encoded.
Apps can decide what to do with all of this information by calling the
`app.SetInitChainer` method.
For instance, let's introduce a `GenesisAccount` struct that can be JSON encoded
and part of a genesis file. Then we can populate the store with such accounts
during InitChain:
```go
TODO
```
If we include a correctly formatted `GenesisAccount` in our Tendermint
genesis.json file, the store will be initialized with those accounts and they'll
be able to send transactions!
## BeginBlock
BeginBlock is called at the beginning of each block, before processing any
transactions with DeliverTx.
It contains information on what validators have signed.
## EndBlock
EndBlock is called at the end of each block, after processing all transactions
with DeliverTx.
It allows the application to return updates to the validator set.
## Commit
Commit is called after EndBlock. It persists the application state and returns
the Merkle root hash to be included in the next Tendermint block. The root hash
can be in Query for Merkle proofs of the state.
## Query
Query allows queries into the application store according to a path.
## CheckTx
CheckTx is used for the mempool. It only runs the AnteHandler. This is so
potentially expensive message handling doesn't begin until the transaction has
actually been committed in a block. The AnteHandler authenticates the sender and
ensures they have enough to pay the fee for the transaction. If the transaction
later fails, the sender still pays the fee.

72
docs/core/app5.md Normal file
View File

@ -0,0 +1,72 @@
# App5 - Basecoin
As we've seen, the SDK provides a flexible yet comprehensive framework for building state
machines and defining their transitions, including authenticating transactions,
executing messages, controlling access to stores, and updating the validator set.
Until now, we have focused on building only isolated ABCI applications to
demonstrate and explain the various features and flexibilities of the SDK.
Here, we'll connect our ABCI application to Tendermint so we can run a full
blockchain node, and introduce command line and HTTP interfaces for interacting with it.
But first, let's talk about how source code should be laid out.
## Directory Structure
TODO
## Tendermint Node
Since the Cosmos-SDK is written in Go, Cosmos-SDK applications can be compiled
with Tendermint into a single binary. Of course, like any ABCI application, they
can also run as separate processes that communicate with Tendermint via socket.
For more details on what's involved in starting a Tendermint full node, see the
[NewNode](https://godoc.org/github.com/tendermint/tendermint/node#NewNode)
function in `github.com/tendermint/tendermint/node`.
The `server` package in the Cosmos-SDK simplifies
connecting an application with a Tendermint node.
For instance, the following `main.go` file will give us a complete full node
using the Basecoin application we built:
```go
//TODO imports
func main() {
cdc := app.MakeCodec()
ctx := server.NewDefaultContext()
rootCmd := &cobra.Command{
Use: "basecoind",
Short: "Basecoin Daemon (server)",
PersistentPreRunE: server.PersistentPreRunEFn(ctx),
}
server.AddCommands(ctx, cdc, rootCmd, server.DefaultAppInit,
server.ConstructAppCreator(newApp, "basecoin"))
// prepare and add flags
rootDir := os.ExpandEnv("$HOME/.basecoind")
executor := cli.PrepareBaseCmd(rootCmd, "BC", rootDir)
executor.Execute()
}
func newApp(logger log.Logger, db dbm.DB) abci.Application {
return app.NewBasecoinApp(logger, db)
}
```
Note we utilize the popular [cobra library](https://github.com/spf13/cobra)
for the CLI, in concert with the [viper library](https://github.com/spf13/library)
for managing configuration. See our [cli library](https://github.com/tendermint/tmlibs/blob/master/cli/setup.go)
for more details.
TODO: compile and run the binary
Options for running the `basecoind` binary are effectively the same as for `tendermint`.
See [Using Tendermint](TODO) for more details.
## Clients
TODO

View File

@ -1,19 +0,0 @@
# BaseApp
The BaseApp is an abstraction over the [Tendermint
ABCI](https://github.com/tendermint/abci) that
simplifies application development by handling common low-level concerns.
It serves as the mediator between the two key components of an SDK app: the store
and the message handlers.
The BaseApp implements the
[`abci.Application`](https://godoc.org/github.com/tendermint/abci/types#Application) interface.
It uses a `MultiStore` to manage the state, a `Router` for transaction handling, and
`Set` methods to specify functions to run at the beginning and end of every
block.
Every SDK app begins with a BaseApp:
```
app := baseapp.NewBaseApp(appName, cdc, logger, db),
```

235
docs/core/examples/app1.go Normal file
View File

@ -0,0 +1,235 @@
package app
import (
"encoding/json"
cmn "github.com/tendermint/tmlibs/common"
dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/log"
bapp "github.com/cosmos/cosmos-sdk/baseapp"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
)
const (
app1Name = "App1"
)
func NewApp1(logger log.Logger, db dbm.DB) *bapp.BaseApp {
cdc := wire.NewCodec()
// Create the base application object.
app := bapp.NewBaseApp(app1Name, cdc, logger, db)
// Create a key for accessing the account store.
keyAccount := sdk.NewKVStoreKey("acc")
// Determine how transactions are decoded.
app.SetTxDecoder(txDecoder)
// Register message routes.
// Note the handler gets access to the account store.
app.Router().
AddRoute("send", handleMsgSend(keyAccount))
// Mount stores and load the latest state.
app.MountStoresIAVL(keyAccount)
err := app.LoadLatestVersion(keyAccount)
if err != nil {
cmn.Exit(err.Error())
}
return app
}
//------------------------------------------------------------------
// Msg
// MsgSend implements sdk.Msg
var _ sdk.Msg = MsgSend{}
// MsgSend to send coins from Input to Output
type MsgSend struct {
From sdk.Address `json:"from"`
To sdk.Address `json:"to"`
Amount sdk.Coins `json:"amount"`
}
// NewMsgSend
func NewMsgSend(from, to sdk.Address, amt sdk.Coins) MsgSend {
return MsgSend{from, to, amt}
}
// Implements Msg.
func (msg MsgSend) Type() string { return "send" }
// Implements Msg. Ensure the addresses are good and the
// amount is positive.
func (msg MsgSend) ValidateBasic() sdk.Error {
if len(msg.From) == 0 {
return sdk.ErrInvalidAddress("From address is empty")
}
if len(msg.To) == 0 {
return sdk.ErrInvalidAddress("To address is empty")
}
if !msg.Amount.IsPositive() {
return sdk.ErrInvalidCoins("Amount is not positive")
}
return nil
}
// Implements Msg. JSON encode the message.
func (msg MsgSend) GetSignBytes() []byte {
bz, err := json.Marshal(msg)
if err != nil {
panic(err)
}
return bz
}
// Implements Msg. Return the signer.
func (msg MsgSend) GetSigners() []sdk.Address {
return []sdk.Address{msg.From}
}
// Returns the sdk.Tags for the message
func (msg MsgSend) Tags() sdk.Tags {
return sdk.NewTags("sender", []byte(msg.From.String())).
AppendTag("receiver", []byte(msg.To.String()))
}
//------------------------------------------------------------------
// Handler for the message
// Handle MsgSend.
// NOTE: msg.From, msg.To, and msg.Amount were already validated
// in ValidateBasic().
func handleMsgSend(key *sdk.KVStoreKey) sdk.Handler {
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
sendMsg, ok := msg.(MsgSend)
if !ok {
// Create custom error message and return result
// Note: Using unreserved error codespace
return sdk.NewError(2, 1, "MsgSend is malformed").Result()
}
// Load the store.
store := ctx.KVStore(key)
// Debit from the sender.
if res := handleFrom(store, sendMsg.From, sendMsg.Amount); !res.IsOK() {
return res
}
// Credit the receiver.
if res := handleTo(store, sendMsg.To, sendMsg.Amount); !res.IsOK() {
return res
}
// Return a success (Code 0).
// Add list of key-value pair descriptors ("tags").
return sdk.Result{
Tags: sendMsg.Tags(),
}
}
}
// Convenience Handlers
func handleFrom(store sdk.KVStore, from sdk.Address, amt sdk.Coins) sdk.Result {
// Get sender account from the store.
accBytes := store.Get(from)
if accBytes == nil {
// Account was not added to store. Return the result of the error.
return sdk.NewError(2, 101, "Account not added to store").Result()
}
// Unmarshal the JSON account bytes.
var acc appAccount
err := json.Unmarshal(accBytes, &acc)
if err != nil {
// InternalError
return sdk.ErrInternal("Error when deserializing account").Result()
}
// Deduct msg amount from sender account.
senderCoins := acc.Coins.Minus(amt)
// If any coin has negative amount, return insufficient coins error.
if !senderCoins.IsNotNegative() {
return sdk.ErrInsufficientCoins("Insufficient coins in account").Result()
}
// Set acc coins to new amount.
acc.Coins = senderCoins
// Encode sender account.
accBytes, err = json.Marshal(acc)
if err != nil {
return sdk.ErrInternal("Account encoding error").Result()
}
// Update store with updated sender account
store.Set(from, accBytes)
return sdk.Result{}
}
func handleTo(store sdk.KVStore, to sdk.Address, amt sdk.Coins) sdk.Result {
// Add msg amount to receiver account
accBytes := store.Get(to)
var acc appAccount
if accBytes == nil {
// Receiver account does not already exist, create a new one.
acc = appAccount{}
} else {
// Receiver account already exists. Retrieve and decode it.
err := json.Unmarshal(accBytes, &acc)
if err != nil {
return sdk.ErrInternal("Account decoding error").Result()
}
}
// Add amount to receiver's old coins
receiverCoins := acc.Coins.Plus(amt)
// Update receiver account
acc.Coins = receiverCoins
// Encode receiver account
accBytes, err := json.Marshal(acc)
if err != nil {
return sdk.ErrInternal("Account encoding error").Result()
}
// Update store with updated receiver account
store.Set(to, accBytes)
return sdk.Result{}
}
// Simple account struct
type appAccount struct {
Coins sdk.Coins `json:"coins"`
}
//------------------------------------------------------------------
// Tx
// Simple tx to wrap the Msg.
type app1Tx struct {
MsgSend
}
// This tx only has one Msg.
func (tx app1Tx) GetMsgs() []sdk.Msg {
return []sdk.Msg{tx.MsgSend}
}
// JSON decode MsgSend.
func txDecoder(txBytes []byte) (sdk.Tx, sdk.Error) {
var tx app1Tx
err := json.Unmarshal(txBytes, &tx)
if err != nil {
return nil, sdk.ErrTxDecode(err.Error())
}
return tx, nil
}

231
docs/core/examples/app2.go Normal file
View File

@ -0,0 +1,231 @@
package app
import (
"bytes"
"encoding/json"
"fmt"
"github.com/tendermint/go-crypto"
cmn "github.com/tendermint/tmlibs/common"
dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/log"
bapp "github.com/cosmos/cosmos-sdk/baseapp"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/auth"
)
const (
app2Name = "App2"
)
var (
issuer = crypto.GenPrivKeyEd25519().PubKey().Address()
)
func NewCodec() *wire.Codec {
cdc := wire.NewCodec()
cdc.RegisterInterface((*sdk.Msg)(nil), nil)
cdc.RegisterConcrete(MsgSend{}, "example/MsgSend", nil)
cdc.RegisterConcrete(MsgIssue{}, "example/MsgIssue", nil)
return cdc
}
func NewApp2(logger log.Logger, db dbm.DB) *bapp.BaseApp {
cdc := NewCodec()
// Create the base application object.
app := bapp.NewBaseApp(app2Name, cdc, logger, db)
// Create a key for accessing the account store.
keyAccount := sdk.NewKVStoreKey("acc")
// Create a key for accessing the issue store.
keyIssue := sdk.NewKVStoreKey("issue")
// set antehandler function
app.SetAnteHandler(antehandler)
// Register message routes.
// Note the handler gets access to the account store.
app.Router().
AddRoute("send", handleMsgSend(keyAccount)).
AddRoute("issue", handleMsgIssue(keyAccount, keyIssue))
// Mount stores and load the latest state.
app.MountStoresIAVL(keyAccount, keyIssue)
err := app.LoadLatestVersion(keyAccount)
if err != nil {
cmn.Exit(err.Error())
}
return app
}
//------------------------------------------------------------------
// Msgs
// MsgIssue to allow a registered issuer
// to issue new coins.
type MsgIssue struct {
Issuer sdk.Address
Receiver sdk.Address
Coin sdk.Coin
}
// Implements Msg.
func (msg MsgIssue) Type() string { return "issue" }
// Implements Msg. Ensures addresses are valid and Coin is positive
func (msg MsgIssue) ValidateBasic() sdk.Error {
if len(msg.Issuer) == 0 {
return sdk.ErrInvalidAddress("Issuer address cannot be empty")
}
if len(msg.Receiver) == 0 {
return sdk.ErrInvalidAddress("Receiver address cannot be empty")
}
// Cannot issue zero or negative coins
if !msg.Coin.IsPositive() {
return sdk.ErrInvalidCoins("Cannot issue 0 or negative coin amounts")
}
return nil
}
// Implements Msg. Get canonical sign bytes for MsgIssue
func (msg MsgIssue) GetSignBytes() []byte {
bz, err := json.Marshal(msg)
if err != nil {
panic(err)
}
return bz
}
// Implements Msg. Return the signer.
func (msg MsgIssue) GetSigners() []sdk.Address {
return []sdk.Address{msg.Issuer}
}
// Returns the sdk.Tags for the message
func (msg MsgIssue) Tags() sdk.Tags {
return sdk.NewTags("issuer", []byte(msg.Issuer.String())).
AppendTag("receiver", []byte(msg.Receiver.String()))
}
//------------------------------------------------------------------
// Handler for the message
// Handle MsgIssue.
func handleMsgIssue(keyIssue *sdk.KVStoreKey, keyAcc *sdk.KVStoreKey) sdk.Handler {
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
issueMsg, ok := msg.(MsgIssue)
if !ok {
return sdk.NewError(2, 1, "MsgIssue is malformed").Result()
}
// Retrieve stores
issueStore := ctx.KVStore(keyIssue)
accStore := ctx.KVStore(keyAcc)
// Handle updating coin info
if res := handleIssuer(issueStore, issueMsg.Issuer, issueMsg.Coin); !res.IsOK() {
return res
}
// Issue coins to receiver using previously defined handleTo function
if res := handleTo(accStore, issueMsg.Receiver, []sdk.Coin{issueMsg.Coin}); !res.IsOK() {
return res
}
return sdk.Result{
// Return result with Issue msg tags
Tags: issueMsg.Tags(),
}
}
}
func handleIssuer(store sdk.KVStore, issuer sdk.Address, coin sdk.Coin) sdk.Result {
// the issuer address is stored directly under the coin denomination
denom := []byte(coin.Denom)
infoBytes := store.Get(denom)
if infoBytes == nil {
return sdk.ErrInvalidCoins(fmt.Sprintf("Unknown coin type %s", coin.Denom)).Result()
}
var coinInfo coinInfo
err := json.Unmarshal(infoBytes, &coinInfo)
if err != nil {
return sdk.ErrInternal("Error when deserializing coinInfo").Result()
}
// Msg Issuer is not authorized to issue these coins
if !bytes.Equal(coinInfo.Issuer, issuer) {
return sdk.ErrUnauthorized(fmt.Sprintf("Msg Issuer cannot issue tokens: %s", coin.Denom)).Result()
}
return sdk.Result{}
}
// coinInfo stores meta data about a coin
type coinInfo struct {
Issuer sdk.Address `json:"issuer"`
}
//------------------------------------------------------------------
// Tx
// Simple tx to wrap the Msg.
type app2Tx struct {
sdk.Msg
Signatures []auth.StdSignature
}
// This tx only has one Msg.
func (tx app2Tx) GetMsgs() []sdk.Msg {
return []sdk.Msg{tx.Msg}
}
func (tx app2Tx) GetSignatures() []auth.StdSignature {
return tx.Signatures
}
//------------------------------------------------------------------
// Simple anteHandler that ensures msg signers have signed.
// Provides no replay protection.
func antehandler(ctx sdk.Context, tx sdk.Tx) (_ sdk.Context, _ sdk.Result, abort bool) {
appTx, ok := tx.(app2Tx)
if !ok {
// set abort boolean to true so that we don't continue to process failed tx
return ctx, sdk.ErrTxDecode("Tx must be of format app2Tx").Result(), true
}
// expect only one msg in app2Tx
msg := tx.GetMsgs()[0]
signerAddrs := msg.GetSigners()
if len(signerAddrs) != len(appTx.GetSignatures()) {
return ctx, sdk.ErrUnauthorized("Number of signatures do not match required amount").Result(), true
}
signBytes := msg.GetSignBytes()
for i, addr := range signerAddrs {
sig := appTx.GetSignatures()[i]
// check that submitted pubkey belongs to required address
if !bytes.Equal(sig.PubKey.Address(), addr) {
return ctx, sdk.ErrUnauthorized("Provided Pubkey does not match required address").Result(), true
}
// check that signature is over expected signBytes
if !sig.PubKey.VerifyBytes(signBytes, sig.Signature) {
return ctx, sdk.ErrUnauthorized("Signature verification failed").Result(), true
}
}
// authentication passed, app to continue processing by sending msg to handler
return ctx, sdk.Result{}, false
}

View File

@ -0,0 +1,49 @@
package app
import (
cmn "github.com/tendermint/tmlibs/common"
dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/log"
bapp "github.com/cosmos/cosmos-sdk/baseapp"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/bank"
)
const (
app3Name = "App3"
)
func NewApp3(logger log.Logger, db dbm.DB) *bapp.BaseApp {
// Create the codec with registered Msg types
cdc := NewCodec()
// Create the base application object.
app := bapp.NewBaseApp(app3Name, cdc, logger, db)
// Create a key for accessing the account store.
keyAccount := sdk.NewKVStoreKey("acc")
keyFees := sdk.NewKVStoreKey("fee") // TODO
// Set various mappers/keepers to interact easily with underlying stores
accountMapper := auth.NewAccountMapper(cdc, keyAccount, &auth.BaseAccount{})
coinKeeper := bank.NewKeeper(accountMapper)
feeKeeper := auth.NewFeeCollectionKeeper(cdc, keyFees)
app.SetAnteHandler(auth.NewAnteHandler(accountMapper, feeKeeper))
// Register message routes.
// Note the handler gets access to
app.Router().
AddRoute("send", bank.NewHandler(coinKeeper))
// Mount stores and load the latest state.
app.MountStoresIAVL(keyAccount, keyFees)
err := app.LoadLatestVersion(keyAccount)
if err != nil {
cmn.Exit(err.Error())
}
return app
}

100
docs/core/examples/app4.go Normal file
View File

@ -0,0 +1,100 @@
package app
import (
abci "github.com/tendermint/abci/types"
cmn "github.com/tendermint/tmlibs/common"
dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/log"
bapp "github.com/cosmos/cosmos-sdk/baseapp"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/bank"
)
const (
app4Name = "App4"
)
func NewApp4(logger log.Logger, db dbm.DB) *bapp.BaseApp {
cdc := NewCodec()
// Create the base application object.
app := bapp.NewBaseApp(app3Name, cdc, logger, db)
// Create a key for accessing the account store.
keyAccount := sdk.NewKVStoreKey("acc")
// Set various mappers/keepers to interact easily with underlying stores
accountMapper := auth.NewAccountMapper(cdc, keyAccount, &auth.BaseAccount{})
coinKeeper := bank.NewKeeper(accountMapper)
// TODO
keyFees := sdk.NewKVStoreKey("fee")
feeKeeper := auth.NewFeeCollectionKeeper(cdc, keyFees)
app.SetAnteHandler(auth.NewAnteHandler(accountMapper, feeKeeper))
// Set InitChainer
app.SetInitChainer(NewInitChainer(cdc, accountMapper))
// Register message routes.
// Note the handler gets access to the account store.
app.Router().
AddRoute("send", bank.NewHandler(coinKeeper))
// Mount stores and load the latest state.
app.MountStoresIAVL(keyAccount, keyFees)
err := app.LoadLatestVersion(keyAccount)
if err != nil {
cmn.Exit(err.Error())
}
return app
}
// Application state at Genesis has accounts with starting balances
type GenesisState struct {
Accounts []*GenesisAccount `json:"accounts"`
}
// GenesisAccount doesn't need pubkey or sequence
type GenesisAccount struct {
Address sdk.Address `json:"address"`
Coins sdk.Coins `json:"coins"`
}
// Converts GenesisAccount to auth.BaseAccount for storage in account store
func (ga *GenesisAccount) ToAccount() (acc *auth.BaseAccount, err error) {
baseAcc := auth.BaseAccount{
Address: ga.Address,
Coins: ga.Coins.Sort(),
}
return &baseAcc, nil
}
// InitChainer will set initial balances for accounts as well as initial coin metadata
// MsgIssue can no longer be used to create new coin
func NewInitChainer(cdc *wire.Codec, accountMapper auth.AccountMapper) sdk.InitChainer {
return func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
stateJSON := req.AppStateBytes
genesisState := new(GenesisState)
err := cdc.UnmarshalJSON(stateJSON, genesisState)
if err != nil {
panic(err)
}
for _, gacc := range genesisState.Accounts {
acc, err := gacc.ToAccount()
if err != nil {
panic(err)
}
acc.AccountNumber = accountMapper.GetNextAccountNumber(ctx)
accountMapper.SetAccount(ctx, acc)
}
return abci.ResponseInitChain{}
}
}

View File

@ -1,52 +0,0 @@
# Message Handling
## Context
The SDK uses a `Context` to propogate common information across functions. The
`Context` is modeled after the Golang `context.Context` object, which has
become ubiquitous in networking middleware and routing applications as a means
to easily propogate request context through handler functions.
The main information stored in the `Context` includes the application
MultiStore, the last block header, and the transaction bytes.
Effectively, the context contains all data that may be necessary for processing
a transaction.
Many methods on SDK objects receive a context as the first argument.
## Handler
Message processing in the SDK is defined through `Handler` functions:
```go
type Handler func(ctx Context, msg Msg) Result
```
A handler takes a context and a message and returns a result. All
information necessary for processing a message should be available in the
context.
While the context holds the entire application state (ie. the
MultiStore), handlers are restricted in what they can do based on the
capabilities they were given when the application was set up.
For instance, suppose we have a `newFooHandler`:
```go
func newFooHandler(key sdk.StoreKey) sdk.Handler {
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
store := ctx.KVStore(key)
// ...
}
}
```
This handler can only access one store based on whichever key its given.
So when we register the handler for the `foo` message type, we make sure
to give it the `fooKey`:
```
app.Router().AddRoute("foo", newFooHandler(fooKey))
```
Now it can only access the `foo` store, but not the `bar` or `cat` stores!

16
docs/core/intro.md Normal file
View File

@ -0,0 +1,16 @@
# Introduction
Welcome to the Cosmos-SDK Core Documentation.
Here you will learn how to use the Cosmos-SDK to build Basecoin, a
complete proof-of-stake cryptocurrency system
We proceed through a series of increasingly advanced and complete implementations of
the Basecoin application, with each implementation showcasing a new component of
the SDK:
- App1 - The Basics - Messages, Stores, Handlers, BaseApp
- App2 - Transactions - Amino and AnteHandler
- App3 - Modules - `x/auth` and `x/bank`
- App4 - Validator Set Changes - Change the Tendermint validator set
- App5 - Basecoin - Bringing it all together

View File

View File

@ -1,76 +0,0 @@
# Messages
Messages are the primary inputs to application state machines.
Developers can create messages containing arbitrary information by
implementing the `Msg` interface:
```go
type Msg interface {
// Return the message type.
// Must be alphanumeric or empty.
Type() string
// Get the canonical byte representation of the Msg.
GetSignBytes() []byte
// ValidateBasic does a simple validation check that
// doesn't require access to any other information.
ValidateBasic() error
// Signers returns the addrs of signers that must sign.
// CONTRACT: All signatures must be present to be valid.
// CONTRACT: Returns addrs in some deterministic order.
GetSigners() []Address
}
```
Messages must specify their type via the `Type()` method. The type should
correspond to the messages handler, so there can be many messages with the same
type.
Messages must also specify how they are to be authenticated. The `GetSigners()`
method return a list of SDK addresses that must sign the message, while the
`GetSignBytes()` method returns the bytes that must be signed for a signature
to be valid.
Addresses in the SDK are arbitrary byte arrays that are hex-encoded when
displayed as a string or rendered in JSON.
Messages can specify basic self-consistency checks using the `ValidateBasic()`
method to enforce that message contents are well formed before any actual logic
begins.
For instance, the `Basecoin` message types are defined in `x/bank/tx.go`:
```go
// Send coins from many inputs to many outputs.
type MsgSend struct {
Inputs []Input `json:"inputs"`
Outputs []Output `json:"outputs"`
}
// Issue new coins to many outputs.
type MsgIssue struct {
Banker sdk.Address `json:"banker"`
Outputs []Output `json:"outputs"`
}
```
Each specifies the addresses that must sign the message:
```go
func (msg MsgSend) GetSigners() []sdk.Address {
addrs := make([]sdk.Address, len(msg.Inputs))
for i, in := range msg.Inputs {
addrs[i] = in.Address
}
return addrs
}
func (msg MsgIssue) GetSigners() []sdk.Address {
return []sdk.Address{msg.Banker}
}
```

View File

@ -1,5 +1,10 @@
# MultiStore
TODO: reconcile this with everything ... would be nice to have this explanation
somewhere but where does it belong ? So far we've already showed how to use it
all by creating KVStore keys and calling app.MountStoresIAVL !
The Cosmos-SDK provides a special Merkle database called a `MultiStore` to be used for all application
storage. The MultiStore consists of multiple Stores that must be mounted to the
MultiStore during application setup. Stores are mounted to the MultiStore using a capabilities key,
@ -14,6 +19,7 @@ The goals of the MultiStore are as follows:
- Merkle proofs for various queries (existence, absence, range, etc.) on current and retained historical state
- Allow for iteration within Stores
- Provide caching for intermediate state during execution of blocks and transactions (including for iteration)
- Support historical state pruning and snapshotting
Currently, all Stores in the MultiStore must satisfy the `KVStore` interface,
@ -55,9 +61,12 @@ through the `Context`.
## Notes
TODO: move this to the spec
In the example above, all IAVL nodes (inner and leaf) will be stored
in mainDB with the prefix of "s/k:foo/" and "s/k:bar/" respectively,
thus sharing the mainDB. All IAVL nodes (inner and leaf) for the
cat KVStore are stored separately in catDB with the prefix of
"s/\_/". The "s/k:KEY/" and "s/\_/" prefixes are there to
disambiguate store items from other items of non-storage concern.

View File

@ -1,154 +0,0 @@
### Transactions
A message is a set of instructions for a state transition.
For a message to be valid, it must be accompanied by at least one
digital signature. The signatures required are determined solely
by the contents of the message.
A transaction is a message with additional information for authentication:
```go
type Tx interface {
GetMsg() Msg
}
```
The standard way to create a transaction from a message is to use the `StdTx` struct defined in the `x/auth` module.
```go
type StdTx struct {
Msg sdk.Msg `json:"msg"`
Fee StdFee `json:"fee"`
Signatures []StdSignature `json:"signatures"`
}
```
The `StdTx.GetSignatures()` method returns a list of signatures, which must match
the list of addresses returned by `tx.Msg.GetSigners()`. The signatures come in
a standard form:
```go
type StdSignature struct {
crypto.PubKey // optional
crypto.Signature
AccountNumber int64
Sequence int64
}
```
It contains the signature itself, as well as the corresponding account's
sequence number. The sequence number is expected to increment every time a
message is signed by a given account. This prevents "replay attacks", where
the same message could be executed over and over again.
The `StdSignature` can also optionally include the public key for verifying the
signature. An application can store the public key for each address it knows
about, making it optional to include the public key in the transaction. In the
case of Basecoin, the public key only needs to be included in the first
transaction send by a given account - after that, the public key is forever
stored by the application and can be left out of transactions.
The address responsible for paying the transactions fee is the first address
returned by msg.GetSigners(). The convenience function `FeePayer(tx Tx)` is provided
to return this.
The standard bytes for signers to sign over is provided by:
```go
func StdSignByes(chainID string, accnums []int64, sequences []int64, fee StdFee, msg sdk.Msg) []byte
```
in `x/auth`. The standard way to construct fees to pay for the processing of transactions is:
```go
// StdFee includes the amount of coins paid in fees and the maximum
// gas to be used by the transaction. The ratio yields an effective "gasprice",
// which must be above some miminum to be accepted into the mempool.
type StdFee struct {
Amount sdk.Coins `json:"amount"`
Gas int64 `json:"gas"`
}
```
### Encoding and Decoding Transactions
Messages and transactions are designed to be generic enough for developers to
specify their own encoding schemes. This enables the SDK to be used as the
framwork for constructing already specified cryptocurrency state machines, for
instance Ethereum.
When initializing an application, a developer can specify a `TxDecoder`
function which determines how an arbitrary byte array should be unmarshalled
into a `Tx`:
```go
type TxDecoder func(txBytes []byte) (Tx, error)
```
The default tx decoder is the Tendermint wire format which uses the go-amino library
for encoding and decoding all message types.
In `Basecoin`, we use the default transaction decoder. The `go-amino` library has the nice
property that it can unmarshal into interface types, but it requires the
relevant types to be registered ahead of type. Registration happens on a
`Codec` object, so as not to taint the global name space.
For instance, in `Basecoin`, we wish to register the `MsgSend` and `MsgIssue`
types:
```go
cdc.RegisterInterface((*sdk.Msg)(nil), nil)
cdc.RegisterConcrete(bank.MsgSend{}, "cosmos-sdk/MsgSend", nil)
cdc.RegisterConcrete(bank.MsgIssue{}, "cosmos-sdk/MsgIssue", nil)
```
Note how each concrete type is given a name - these name determine the type's
unique "prefix bytes" during encoding. A registered type will always use the
same prefix-bytes, regardless of what interface it is satisfying. For more
details, see the [go-amino documentation](https://github.com/tendermint/go-amino/blob/develop).
If you wish to use a custom encoding scheme, you must define a TxDecoder function
and set it as the decoder in your extended baseapp using the `SetTxDecoder(decoder sdk.TxDecoder)`.
Ex:
```go
app.SetTxDecoder(CustomTxDecodeFn)
```
## AnteHandler
The AnteHandler is used to do all transaction-level processing (i.e. Fee payment, signature verification)
before passing the message to its respective handler.
```go
type AnteHandler func(ctx Context, tx Tx) (newCtx Context, result Result, abort bool)
```
The antehandler takes a Context and a transaction and returns a new Context, a Result, and the abort boolean.
As with the handler, all information necessary for processing a message should be available in the
context.
If the transaction fails, then the application should not waste time processing the message. Thus, the antehandler should
return an Error's Result method and set the abort boolean to `true` so that the application knows not to process the message in a handler.
Most applications can use the provided antehandler implementation in `x/auth` which handles signature verification
as well as collecting fees.
Note: Signatures must be over `auth.StdSignDoc` introduced above to use the provided antehandler.
```go
// File: cosmos-sdk/examples/basecoin/app/app.go
app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper, app.feeCollectionKeeper))
```
### Handling Fee payment
### Handling Authentication
The antehandler is responsible for handling all authentication of a transaction before passing the message onto its handler.
This generally involves signature verification. The antehandler should check that all of the addresses that are returned in
`tx.GetMsg().GetSigners()` signed the message and that they signed over `tx.GetMsg().GetSignBytes()`.

51
docs/modules/README.md Normal file
View File

@ -0,0 +1,51 @@
# Bank
The `x/bank` module is for transferring coins between accounts.
See the [API docs](https://godoc.org/github.com/cosmos/cosmos-sdk/x/bank).
# Stake
The `x/stake` module is for Cosmos Delegated-Proof-of-Stake.
See the [API docs](https://godoc.org/github.com/cosmos/cosmos-sdk/x/stake).
See the
[specification](https://github.com/cosmos/cosmos-sdk/tree/develop/docs/spec/staking)
# Slashing
The `x/slashing` module is for Cosmos Delegated-Proof-of-Stake.
See the [API docs](https://godoc.org/github.com/cosmos/cosmos-sdk/x/slashing)
See the
[specification](https://github.com/cosmos/cosmos-sdk/tree/develop/docs/spec/slashing)
# Provisions
The `x/provisions` module is for distributing fees and inflation across bonded
stakeholders.
See the [API docs](https://godoc.org/github.com/cosmos/cosmos-sdk/x/provisions)
See the
[specification](https://github.com/cosmos/cosmos-sdk/tree/develop/docs/spec/provisions)
# Governance
The `x/gov` module is for bonded stakeholders to make proposals and vote on them.
See the [API docs](https://godoc.org/github.com/cosmos/cosmos-sdk/x/gov)
See the
[specification](https://github.com/cosmos/cosmos-sdk/tree/develop/docs/spec/governance)
# IBC
The `x/ibc` module is for InterBlockchain Communication.
See the [API docs](https://godoc.org/github.com/cosmos/cosmos-sdk/x/ibc)
See the
[specification](https://github.com/cosmos/cosmos-sdk/tree/develop/docs/spec/ibc)

View File

@ -1,7 +1,8 @@
package types
// core function variable which application runs for transactions
// Handler defines the core of the state transition function of an application.
type Handler func(ctx Context, msg Msg) Result
// AnteHandler authenticates transactions, before their internal messages are handled.
// If newCtx.IsZero(), ctx is used instead.
type AnteHandler func(ctx Context, tx Tx) (newCtx Context, result Result, abort bool)

View File

@ -11,13 +11,13 @@ type Msg interface {
// Must be alphanumeric or empty.
Type() string
// Get the canonical byte representation of the Msg.
GetSignBytes() []byte
// ValidateBasic does a simple validation check that
// doesn't require access to any other information.
ValidateBasic() Error
// Get the canonical byte representation of the Msg.
GetSignBytes() []byte
// Signers returns the addrs of signers that must sign.
// CONTRACT: All signatures must be present to be valid.
// CONTRACT: Returns addrs in some deterministic order.