cleanup _attic

This commit is contained in:
Ethan Buchman
2018-06-29 01:53:43 -04:00
parent 12a180786a
commit 822ebdb501
14 changed files with 712 additions and 1666 deletions
+402
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.
-204
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
```
-->
-83
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.
+216
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`
-64
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``.
+675
View File
@@ -0,0 +1,675 @@
# Stake Module
## Overview
The stake module is tasked with various core staking functionality. Through the
stake module atoms may be bonded, delegated, and provisions/rewards are
distributed. Atom provisions are distributed to validators and their delegators
through share distribution of a collective pool of all staked atoms. As atoms
are created they are added to the common pool and each share become
proportionally worth more atoms. Fees are distributed through a similar pooling
mechanism but where each validator and delegator maintains an adjustment factor
to determine the true proportion of fees they are entitled too. This adjustment
factor is updated for each delegator and validator for each block where changes
to the voting power occurs in the network. Broken down, the stake module at a
high level is responsible for:
- Declaration of candidacy for becoming a validator
- Updating Tendermint validating power to reflect slashable stake
- Delegation and unbonding transactions
- Implementing unbonding period
- Provisioning Atoms
- Managing and distributing transaction fees
- Providing the framework for validator commission on delegators
### Transaction Overview
Available Transactions:
- TxDeclareCandidacy
- TxEditCandidacy
- TxLivelinessCheck
- TxProveLive
- TxDelegate
- TxUnbond
- TxRedelegate
## Global State
`Params` and `GlobalState` represent the global persistent state of Gaia.
`Params` is intended to remain static whereas `GlobalState` is anticipated to
change each block.
``` golang
type Params struct {
HoldBonded Address // account where all bonded coins are held
HoldUnbonded Address // account where all delegated but unbonded coins are held
InflationRateChange rational.Rational // maximum annual change in inflation rate
InflationMax rational.Rational // maximum inflation rate
InflationMin rational.Rational // minimum inflation rate
GoalBonded rational.Rational // Goal of percent bonded atoms
ReserveTax rational.Rational // Tax collected on all fees
MaxVals uint16 // maximum number of validators
AllowedBondDenom string // bondable coin denomination
// gas costs for txs
GasDeclareCandidacy int64
GasEditCandidacy int64
GasDelegate int64
GasRedelegate int64
GasUnbond int64
}
```
``` golang
type GlobalState struct {
TotalSupply int64 // total supply of atom tokens
BondedShares rational.Rat // sum of all shares distributed for the BondedPool
UnbondedShares rational.Rat // sum of all shares distributed for the UnbondedPool
BondedPool int64 // reserve of bonded tokens
UnbondedPool int64 // reserve of unbonded tokens held with candidates
InflationLastTime int64 // timestamp of last processing of inflation
Inflation rational.Rat // current annual inflation rate
DateLastCommissionReset int64 // unix timestamp for last commission accounting reset
FeePool coin.Coins // fee pool for all the fee shares which have already been distributed
ReservePool coin.Coins // pool of reserve taxes collected on all fees for governance use
Adjustment rational.Rat // Adjustment factor for calculating global fee accum
}
```
### The Queue
The queue is ordered so the next to unbond/re-delegate is at the head. Every
tick the head of the queue is checked and if the unbonding period has passed
since `InitHeight` commence with final settlement of the unbonding and pop the
queue. All queue elements used for unbonding share a common struct:
``` golang
type QueueElem struct {
Candidate crypto.PubKey
InitHeight int64 // when the queue was initiated
}
```
Each `QueueElem` is persisted in the store until it is popped from the queue.
## Validator-Candidate
The `Candidate` struct holds the current state and some historical actions of
validators or candidate-validators.
``` golang
type Candidate struct {
Status CandidateStatus
PubKey crypto.PubKey
GovernancePubKey crypto.PubKey
Owner Address
GlobalStakeShares rational.Rat
IssuedDelegatorShares rational.Rat
RedelegatingShares rational.Rat
VotingPower rational.Rat
Commission rational.Rat
CommissionMax rational.Rat
CommissionChangeRate rational.Rat
CommissionChangeToday rational.Rat
ProposerRewardPool coin.Coins
Adjustment rational.Rat
Description Description
}
type CandidateStatus byte
const (
VyingUnbonded CandidateStatus = 0x00
VyingUnbonding CandidateStatus = 0x01
Bonded CandidateStatus = 0x02
KickUnbonding CandidateStatus = 0x03
KickUnbonded CandidateStatus = 0x04
)
type Description struct {
Name string
DateBonded string
Identity string
Website string
Details string
}
```
Candidate parameters are described:
- Status: signal that the candidate is either vying for validator status
either unbonded or unbonding, an active validator, or a kicked validator
either unbonding or unbonded.
- PubKey: separated key from the owner of the candidate as is used strictly
for participating in consensus.
- Owner: Address where coins are bonded from and unbonded to
- GlobalStakeShares: Represents shares of `GlobalState.BondedPool` if
`Candidate.Status` is `Bonded`; or shares of `GlobalState.UnbondedPool` if
`Candidate.Status` is otherwise
- IssuedDelegatorShares: Sum of all shares issued to delegators (which
includes the candidate's self-bond) which represent each of their stake in
the Candidate's `GlobalStakeShares`
- RedelegatingShares: The portion of `IssuedDelegatorShares` which are
currently re-delegating to a new validator
- VotingPower: Proportional to the amount of bonded tokens which the validator
has if the validator is within the top 100 validators.
- Commission: The commission rate of fees charged to any delegators
- CommissionMax: The maximum commission rate which this candidate can charge
each day from the date `GlobalState.DateLastCommissionReset`
- CommissionChangeRate: The maximum daily increase of the candidate commission
- CommissionChangeToday: Counter for the amount of change to commission rate
which has occurred today, reset on the first block of each day (UTC time)
- ProposerRewardPool: reward pool for extra fees collected when this candidate
is the proposer of a block
- Adjustment factor used to passively calculate each validators entitled fees
from `GlobalState.FeePool`
- Description
- Name: moniker
- DateBonded: date determined which the validator was bonded
- Identity: optional field to provide a signature which verifies the
validators identity (ex. UPort or Keybase)
- Website: optional website link
- Details: optional details
validator candidacy can be declared using the `TxDeclareCandidacy` transaction.
During this transaction a self-delegation transaction is executed to bond
tokens which are sent in with the transaction.
``` golang
type TxDeclareCandidacy struct {
PubKey crypto.PubKey
Amount coin.Coin
GovernancePubKey crypto.PubKey
Commission rational.Rat
CommissionMax int64
CommissionMaxChange int64
Description Description
}
```
For all subsequent self-bonding, whether self-bonding or delegation the
`TxDelegate` function should be used. In this context `TxUnbond` is used to
unbond either delegation bonds or validator self-bonds.
If either the `Description` (excluding `DateBonded` which is constant),
`Commission`, or the `GovernancePubKey` need to be updated, the
`TxEditCandidacy` transaction should be sent from the owner account:
``` golang
type TxEditCandidacy struct {
GovernancePubKey crypto.PubKey
Commission int64
Description Description
}
```
### Persistent State
Within the store, each `Candidate` is stored by validator-pubkey.
- key: validator-pubkey
- value: `Candidate` object
A second key-value pair is also persisted in order to quickly sort though the
group of all candidates, this second index is however not persisted through the
merkle store.
- key: `Candidate.GlobalStakeShares`
- value: `Candidate.PubKey`
When the set of all validators needs to be determined from the group of all
candidates, the top candidates, sorted by GlobalStakeShares can be retrieved
from this sorting without the need to retrieve the entire group of candidates.
When validators are kicked from the validator set they are removed from this
list.
### New Validators
The validator set is updated in the first block of every hour. Validators are
taken as the first `GlobalState.MaxValidators` number of candidates with the
greatest amount of staked atoms who have not been kicked from the validator
set.
### Kicked Validators
Unbonding of an entire validator-candidate to a temporary liquid account occurs
under the scenarios:
- not enough stake to be within the validator set
- the owner unbonds all of their staked tokens
- validator liveliness issues
- crosses a self-imposed safety threshold
- minimum number of tokens staked by owner
- minimum ratio of tokens staked by owner to delegator tokens
When this occurs delegator's tokens do not unbond to their personal wallets but
begin the unbonding process to a pool where they must then transact in order to
withdraw to their respective wallets. The following unbonding will use the
following queue element
``` golang
type QueueElemUnbondCandidate struct {
QueueElem
}
```
If a delegator chooses to initiate an unbond or re-delegation of their shares
while a candidate-unbond is commencing, then that unbond/re-delegation is
subject to a reduced unbonding period based on how much time those funds have
already spent in the unbonding queue.
#### Liveliness issues
Liveliness issues are calculated by keeping track of the block precommits in
the block header. A queue is persisted which contains the block headers from
all recent blocks for the duration of the unbonding period. A validator is
defined as having livliness issues if they have not been included in more than
33% of the blocks over:
- The most recent 24 Hours if they have >= 20% of global stake
- The most recent week if they have = 0% of global stake
- Linear interpolation of the above two scenarios
Liveliness kicks are only checked when a `TxLivelinessCheck` transaction is
submitted.
``` golang
type TxLivelinessCheck struct {
PubKey crypto.PubKey
RewardAccount Addresss
}
```
If the `TxLivelinessCheck is successful in kicking a validator, 5% of the
liveliness punishment is provided as a reward to `RewardAccount`.
#### Validator Liveliness Proof
If the validator was kicked for liveliness issues and is able to regain
liveliness then all delegators in the temporary unbonding pool which have not
transacted to move will be bonded back to the now-live validator and begin to
once again collect provisions and rewards. Regaining livliness is demonstrated
by sending in a `TxProveLive` transaction:
``` golang
type TxProveLive struct {
PubKey crypto.PubKey
}
```
## Delegator bond
Atom holders may delegate coins to validators, under this circumstance their
funds are held in a `DelegatorBond`. It is owned by one delegator, and is
associated with the shares for one validator. The sender of the transaction is
considered to be the owner of the bond,
``` golang
type DelegatorBond struct {
Candidate crypto.PubKey
Shares rational.Rat
AdjustmentFeePool coin.Coins
AdjustmentRewardPool coin.Coins
}
```
Description:
- Candidate: pubkey of the validator candidate: bonding too
- Shares: the number of shares received from the validator candidate
- AdjustmentFeePool: Adjustment factor used to passively calculate each bonds
entitled fees from `GlobalState.FeePool`
- AdjustmentRewardPool: Adjustment factor used to passively calculate each
bonds entitled fees from `Candidate.ProposerRewardPool``
Each `DelegatorBond` is individually indexed within the store by delegator
address and candidate pubkey.
- key: Delegator and Candidate-Pubkey
- value: DelegatorBond
### Delegating
Delegator bonds are created using the TxDelegate transaction. Within this
transaction the validator candidate queried with an amount of coins, whereby
given the current exchange rate of candidate's delegator-shares-to-atoms the
candidate will return shares which are assigned in `DelegatorBond.Shares`.
``` golang
type TxDelegate struct {
PubKey crypto.PubKey
Amount coin.Coin
}
```
### Unbonding
Delegator unbonding is defined by the following transaction type:
``` golang
type TxUnbond struct {
PubKey crypto.PubKey
Shares rational.Rat
}
```
When unbonding is initiated, delegator shares are immediately removed from the
candidate and added to a queue object.
``` golang
type QueueElemUnbondDelegation struct {
QueueElem
Payout Address // account to pay out to
Shares rational.Rat // amount of shares which are unbonding
StartSlashRatio rational.Rat // candidate slash ratio at start of re-delegation
}
```
In the unbonding queue - the fraction of all historical slashings on
that validator are recorded (`StartSlashRatio`). When this queue reaches maturity
if that total slashing applied is greater on the validator then the
difference (amount that should have been slashed from the first validator) is
assigned to the amount being paid out.
### Re-Delegation
The re-delegation command allows delegators to switch validators while still
receiving equal reward to as if you had never unbonded.
``` golang
type TxRedelegate struct {
PubKeyFrom crypto.PubKey
PubKeyTo crypto.PubKey
Shares rational.Rat
}
```
When re-delegation is initiated, delegator shares remain accounted for within
the `Candidate.Shares`, the term `RedelegatingShares` is incremented and a
queue element is created.
``` golang
type QueueElemReDelegate struct {
QueueElem
Payout Address // account to pay out to
Shares rational.Rat // amount of shares which are unbonding
NewCandidate crypto.PubKey // validator to bond to after unbond
}
```
During the unbonding period all unbonding shares do not count towards the
voting power of a validator. Once the `QueueElemReDelegation` has reached
maturity, the appropriate unbonding shares are removed from the `Shares` and
`RedelegatingShares` term.
Note that with the current menchanism a delegator cannot redelegate funds which
are currently redelegating.
### Cancel Unbonding
A delegator who is in the process of unbonding from a validator may use the
re-delegate transaction to bond back to the original validator they're
currently unbonding from (and only that validator). If initiated, the delegator
will immediately begin to one again collect rewards from their validator.
## Provision Calculations
Every hour atom provisions are assigned proportionally to the each slashable
bonded token which includes re-delegating atoms but not unbonding tokens.
Validation provisions are payed directly to a global hold account
(`BondedTokenPool`) and proportions of that hold account owned by each
validator is defined as the `GlobalStakeBonded`. The tokens are payed as bonded
tokens.
Here, the bonded tokens that a candidate has can be calculated as:
```
globalStakeExRate = params.BondedTokenPool / params.IssuedGlobalStakeShares
candidateCoins = candidate.GlobalStakeShares * globalStakeExRate
```
If a delegator chooses to add more tokens to a validator then the amount of
validator shares distributed is calculated on exchange rate (aka every
delegators shares do not change value at that moment. The validator's
accounting of distributed shares to delegators must also increased at every
deposit.
```
delegatorExRate = validatorCoins / candidate.IssuedDelegatorShares
createShares = coinsDeposited / delegatorExRate
candidate.IssuedDelegatorShares += createShares
```
Whenever a validator has new tokens added to it, the `BondedTokenPool` is
increased and must be reflected in the global parameter as well as the
validators `GlobalStakeShares`. This calculation ensures that the worth of the
`GlobalStakeShares` of other validators remains worth a constant absolute
amount of the `BondedTokenPool`
```
createdGlobalStakeShares = coinsDeposited / globalStakeExRate
validator.GlobalStakeShares += createdGlobalStakeShares
params.IssuedGlobalStakeShares += createdGlobalStakeShares
params.BondedTokenPool += coinsDeposited
```
Similarly, if a delegator wanted to unbond coins:
```
coinsWithdrawn = withdrawlShares * delegatorExRate
destroyedGlobalStakeShares = coinsWithdrawn / globalStakeExRate
validator.GlobalStakeShares -= destroyedGlobalStakeShares
params.IssuedGlobalStakeShares -= destroyedGlobalStakeShares
params.BondedTokenPool -= coinsWithdrawn
```
Note that when an re-delegation occurs the shares to move are placed in an
re-delegation queue where they continue to collect validator provisions until
queue element matures. Although provisions are collected during re-delegation,
re-delegation tokens do not contribute to the voting power of a validator.
Validator provisions are minted on an hourly basis (the first block of a new
hour). The annual target of between 7% and 20%. The long-term target ratio of
bonded tokens to unbonded tokens is 67%.
The target annual inflation rate is recalculated for each previsions cycle. The
inflation is also subject to a rate change (positive of negative) depending or
the distance from the desired ratio (67%). The maximum rate change possible is
defined to be 13% per year, however the annual inflation is capped as between
7% and 20%.
```
inflationRateChange(0) = 0
annualInflation(0) = 0.07
bondedRatio = bondedTokenPool / totalTokenSupply
AnnualInflationRateChange = (1 - bondedRatio / 0.67) * 0.13
annualInflation += AnnualInflationRateChange
if annualInflation > 0.20 then annualInflation = 0.20
if annualInflation < 0.07 then annualInflation = 0.07
provisionTokensHourly = totalTokenSupply * annualInflation / (365.25*24)
```
Because the validators hold a relative bonded share (`GlobalStakeShare`), when
more bonded tokens are added proportionally to all validators the only term
which needs to be updated is the `BondedTokenPool`. So for each previsions
cycle:
```
params.BondedTokenPool += provisionTokensHourly
```
## Fee Calculations
Collected fees are pooled globally and divided out passively to validators and
delegators. Each validator has the opportunity to charge commission to the
delegators on the fees collected on behalf of the delegators by the validators.
Fees are paid directly into a global fee pool. Due to the nature of of passive
accounting whenever changes to parameters which affect the rate of fee
distribution occurs, withdrawal of fees must also occur.
- when withdrawing one must withdrawal the maximum amount they are entitled
too, leaving nothing in the pool,
- when bonding, unbonding, or re-delegating tokens to an existing account a
full withdrawal of the fees must occur (as the rules for lazy accounting
change),
- when a candidate chooses to change the commission on fees, all accumulated
commission fees must be simultaneously withdrawn.
When the validator is the proposer of the round, that validator (and their
delegators) receives between 1% and 5% of fee rewards, the reserve tax is then
charged, then the remainder is distributed socially by voting power to all
validators including the proposer validator. The amount of proposer reward is
calculated from pre-commits Tendermint messages. All provision rewards are
added to a provision reward pool which validator holds individually. Here note
that `BondedShares` represents the sum of all voting power saved in the
`GlobalState` (denoted `gs`).
```
proposerReward = feesCollected * (0.01 + 0.04
* sumOfVotingPowerOfPrecommitValidators / gs.BondedShares)
candidate.ProposerRewardPool += proposerReward
reserveTaxed = feesCollected * params.ReserveTax
gs.ReservePool += reserveTaxed
distributedReward = feesCollected - proposerReward - reserveTaxed
gs.FeePool += distributedReward
gs.SumFeesReceived += distributedReward
gs.RecentFee = distributedReward
```
The entitlement to the fee pool held by the each validator can be accounted for
lazily. First we must account for a candidate's `count` and `adjustment`. The
`count` represents a lazy accounting of what that candidates entitlement to the
fee pool would be if there `VotingPower` was to never change and they were to
never withdraw fees.
```
candidate.count = candidate.VotingPower * BlockHeight
```
Similarly the GlobalState count can be passively calculated whenever needed,
where `BondedShares` is the updated sum of voting powers from all validators.
```
gs.count = gs.BondedShares * BlockHeight
```
The `adjustment` term accounts for changes in voting power and withdrawals of
fees. The adjustment factor must be persisted with the candidate and modified
whenever fees are withdrawn from the candidate or the voting power of the
candidate changes. When the voting power of the candidate changes the
`Adjustment` factor is increased/decreased by the cumulative difference in the
voting power if the voting power has been the new voting power as opposed to
the old voting power for the entire duration of the blockchain up the previous
block. Each time there is an adjustment change the GlobalState (denoted `gs`)
`Adjustment` must also be updated.
```
simplePool = candidate.count / gs.count * gs.SumFeesReceived
projectedPool = candidate.PrevPower * (height-1)
/ (gs.PrevPower * (height-1)) * gs.PrevFeesReceived
+ candidate.Power / gs.Power * gs.RecentFee
AdjustmentChange = simplePool - projectedPool
candidate.AdjustmentRewardPool += AdjustmentChange
gs.Adjustment += AdjustmentChange
```
Every instance that the voting power changes, information about the state of
the validator set during the change must be recorded as a `powerChange` for
other validators to run through. Before any validator modifies its voting power
it must first run through the above calculation to determine the change in
their `caandidate.AdjustmentRewardPool` for all historical changes in the set
of `powerChange` which they have not yet synced to. The set of all
`powerChange` may be trimmed from its oldest members once all validators have
synced past the height of the oldest `powerChange`. This trim procedure will
occur on an epoch basis.
```golang
type powerChange struct {
height int64 // block height at change
power rational.Rat // total power at change
prevpower rational.Rat // total power at previous height-1
feesin coins.Coin // fees in at block height
prevFeePool coins.Coin // total fees in at previous block height
}
```
Note that the adjustment factor may result as negative if the voting power of a
different candidate has decreased.
```
candidate.AdjustmentRewardPool += withdrawn
gs.Adjustment += withdrawn
```
Now the entitled fee pool of each candidate can be lazily accounted for at
any given block:
```
candidate.feePool = candidate.simplePool - candidate.Adjustment
```
So far we have covered two sources fees which can be withdrawn from: Fees from
proposer rewards (`candidate.ProposerRewardPool`), and fees from the fee pool
(`candidate.feePool`). However we should note that all fees from fee pool are
subject to commission rate from the owner of the candidate. These next
calculations outline the math behind withdrawing fee rewards as either a
delegator to a candidate providing commission, or as the owner of a candidate
who is receiving commission.
### Calculations For Delegators and Candidates
The same mechanism described to calculate the fees which an entire validator is
entitled to is be applied to delegator level to determine the entitled fees for
each delegator and the candidates entitled commission from `gs.FeesPool` and
`candidate.ProposerRewardPool`.
The calculations are identical with a few modifications to the parameters:
- Delegator's entitlement to `gs.FeePool`:
- entitled party voting power should be taken as the effective voting power
after commission is retrieved,
`bond.Shares/candidate.TotalDelegatorShares * candidate.VotingPower * (1 - candidate.Commission)`
- Delegator's entitlement to `candidate.ProposerFeePool`
- global power in this context is actually shares
`candidate.TotalDelegatorShares`
- entitled party voting power should be taken as the effective shares after
commission is retrieved, `bond.Shares * (1 - candidate.Commission)`
- Candidate's commission entitlement to `gs.FeePool`
- entitled party voting power should be taken as the effective voting power
of commission portion of total voting power,
`candidate.VotingPower * candidate.Commission`
- Candidate's commission entitlement to `candidate.ProposerFeePool`
- global power in this context is actually shares
`candidate.TotalDelegatorShares`
- entitled party voting power should be taken as the of commission portion
of total delegators shares,
`candidate.TotalDelegatorShares * candidate.Commission`
For more implementation ideas see spreadsheet `spec/AbsoluteFeeDistrModel.xlsx`
As mentioned earlier, every time the voting power of a delegator bond is
changing either by unbonding or further bonding, all fees must be
simultaneously withdrawn. Similarly if the validator changes the commission
rate, all commission on fees must be simultaneously withdrawn.
### Other general notes on fees accounting
- When a delegator chooses to re-delegate shares, fees continue to accumulate
until the re-delegation queue reaches maturity. At the block which the queue
reaches maturity and shares are re-delegated all available fees are
simultaneously withdrawn.
- Whenever a totally new validator is added to the validator set, the `accum`
of the entire candidate must be 0, meaning that the initial value for
`candidate.Adjustment` must be set to the value of `canidate.Count` for the
height which the candidate is added on the validator set.
- The feePool of a new delegator bond will be 0 for the height at which the bond
was added. This is achieved by setting `DelegatorBond.FeeWithdrawalHeight` to
the height which the bond was added.
+698
View File
@@ -0,0 +1,698 @@
# Stake Module
## Overview
The stake module is tasked with various core staking functionality,
including validator set rotation, unbonding periods, and the
distribution of inflationary provisions and transaction fees.
It is designed to efficiently facilitate small numbers of
validators (hundreds), and large numbers of delegators (tens of thousands).
Bonded Atoms are pooled globally and for each validator.
Validators have shares in the global pool, and delegators
have shares in the pool of every validator they delegate to.
Atom provisions simply accumulate in the global pool, making
each share worth proportionally more.
Validator shares can be redeemed for Atoms, but the Atoms will be locked in a queue
for an unbonding period before they can be withdrawn to an account.
Delegators can exchange one validator's shares for another immediately
(ie. they can re-delegate to another validator), but must then wait the
unbonding period before they can do it again.
Fees are pooled separately and withdrawn lazily, at any time.
They are not bonded, and can be paid in multiple tokens.
An adjustment factor is maintained for each validator
and delegator to determine the true proportion of fees in the pool they are entitled too.
Adjustment factors are updated every time a validator or delegator's voting power changes.
Validators and delegators must withdraw all fees they are entitled too before they can bond or
unbond Atoms.
## State
The staking module persists the following to the store:
- `GlobalState`, describing the global pools
- a `Candidate` for each candidate validator, indexed by public key
- a `Candidate` for each candidate validator, indexed by shares in the global pool (ie. ordered)
- a `DelegatorBond` for each delegation to a candidate by a delegator, indexed by delegator and candidate
public keys
- a `Queue` of unbonding delegations (TODO)
### Global State
``` golang
type GlobalState struct {
TotalSupply int64 // total supply of atom tokens
BondedShares rational.Rat // sum of all shares distributed for the BondedPool
UnbondedShares rational.Rat // sum of all shares distributed for the UnbondedPool
BondedPool int64 // reserve of bonded tokens
UnbondedPool int64 // reserve of unbonded tokens held with candidates
InflationLastTime int64 // timestamp of last processing of inflation
Inflation rational.Rat // current annual inflation rate
DateLastCommissionReset int64 // unix timestamp for last commission accounting reset
FeePool coin.Coins // fee pool for all the fee shares which have already been distributed
ReservePool coin.Coins // pool of reserve taxes collected on all fees for governance use
Adjustment rational.Rat // Adjustment factor for calculating global fee accum
}
```
### Candidate
The `Candidate` struct holds the current state and some historical actions of
validators or candidate-validators.
``` golang
type Candidate struct {
Status CandidateStatus
PubKey crypto.PubKey
GovernancePubKey crypto.PubKey
Owner Address
GlobalStakeShares rational.Rat
IssuedDelegatorShares rational.Rat
RedelegatingShares rational.Rat
VotingPower rational.Rat
Commission rational.Rat
CommissionMax rational.Rat
CommissionChangeRate rational.Rat
CommissionChangeToday rational.Rat
ProposerRewardPool coin.Coins
Adjustment rational.Rat
Description Description
}
type CandidateStatus byte
const (
VyingUnbonded CandidateStatus = 0x00
VyingUnbonding CandidateStatus = 0x01
Bonded CandidateStatus = 0x02
KickUnbonding CandidateStatus = 0x03
KickUnbonded CandidateStatus = 0x04
)
type Description struct {
Name string
DateBonded string
Identity string
Website string
Details string
}
```
Candidate parameters are described:
- Status: signal that the candidate is either vying for validator status
either unbonded or unbonding, an active validator, or a kicked validator
either unbonding or unbonded.
- PubKey: separated key from the owner of the candidate as is used strictly
for participating in consensus.
- Owner: Address where coins are bonded from and unbonded to
- GlobalStakeShares: Represents shares of `GlobalState.BondedPool` if
`Candidate.Status` is `Bonded`; or shares of `GlobalState.UnbondedPool` if
`Candidate.Status` is otherwise
- IssuedDelegatorShares: Sum of all shares issued to delegators (which
includes the candidate's self-bond) which represent each of their stake in
the Candidate's `GlobalStakeShares`
- RedelegatingShares: The portion of `IssuedDelegatorShares` which are
currently re-delegating to a new validator
- VotingPower: Proportional to the amount of bonded tokens which the validator
has if the validator is within the top 100 validators.
- Commission: The commission rate of fees charged to any delegators
- CommissionMax: The maximum commission rate which this candidate can charge
each day from the date `GlobalState.DateLastCommissionReset`
- CommissionChangeRate: The maximum daily increase of the candidate commission
- CommissionChangeToday: Counter for the amount of change to commission rate
which has occurred today, reset on the first block of each day (UTC time)
- ProposerRewardPool: reward pool for extra fees collected when this candidate
is the proposer of a block
- Adjustment factor used to passively calculate each validators entitled fees
from `GlobalState.FeePool`
- Description
- Name: moniker
- DateBonded: date determined which the validator was bonded
- Identity: optional field to provide a signature which verifies the
validators identity (ex. UPort or Keybase)
- Website: optional website link
- Details: optional details
Candidates are indexed by their `Candidate.PubKey`.
Additionally, we index empty values by the candidates global stake shares concatenated with the public key.
TODO: be more precise.
When the set of all validators needs to be determined from the group of all
candidates, the top candidates, sorted by GlobalStakeShares can be retrieved
from this sorting without the need to retrieve the entire group of candidates.
When validators are kicked from the validator set they are removed from this
list.
### DelegatorBond
Atom holders may delegate coins to validators, under this circumstance their
funds are held in a `DelegatorBond`. It is owned by one delegator, and is
associated with the shares for one validator. The sender of the transaction is
considered to be the owner of the bond,
``` golang
type DelegatorBond struct {
Candidate crypto.PubKey
Shares rational.Rat
AdjustmentFeePool coin.Coins
AdjustmentRewardPool coin.Coins
}
```
Description:
- Candidate: pubkey of the validator candidate: bonding too
- Shares: the number of shares received from the validator candidate
- AdjustmentFeePool: Adjustment factor used to passively calculate each bonds
entitled fees from `GlobalState.FeePool`
- AdjustmentRewardPool: Adjustment factor used to passively calculate each
bonds entitled fees from `Candidate.ProposerRewardPool``
Each `DelegatorBond` is individually indexed within the store by delegator
address and candidate pubkey.
- key: Delegator and Candidate-Pubkey
- value: DelegatorBond
### Unbonding Queue
- main unbonding queue contains both UnbondElem and RedelegateElem
- "queue" + <i>
- new unbonding queue every time a val leaves the validator set
- "queue"+ <candidate.pubkey > + <i>
The queue is ordered so the next to unbond/re-delegate is at the head. Every
tick the head of the queue is checked and if the unbonding period has passed
since `InitHeight` commence with final settlement of the unbonding and pop the
queue. All queue elements used for unbonding share a common struct:
``` golang
type QueueElem struct {
Candidate crypto.PubKey
InitHeight int64 // when the queue was initiated
}
```
``` golang
type QueueElemUnbondCandidate struct {
QueueElem
}
```
``` golang
type QueueElemUnbondDelegation struct {
QueueElem
Payout Address // account to pay out to
Shares rational.Rat // amount of shares which are unbonding
StartSlashRatio rational.Rat // candidate slash ratio at start of re-delegation
}
```
``` golang
type QueueElemReDelegate struct {
QueueElem
Payout Address // account to pay out to
Shares rational.Rat // amount of shares which are unbonding
NewCandidate crypto.PubKey // validator to bond to after unbond
}
```
Each `QueueElem` is persisted in the store until it is popped from the queue.
## Transactions
### TxDeclareCandidacy
Validator candidacy can be declared using the `TxDeclareCandidacy` transaction.
During this transaction a self-delegation transaction is executed to bond
tokens which are sent in with the transaction.
``` golang
type TxDeclareCandidacy struct {
PubKey crypto.PubKey
Amount coin.Coin
GovernancePubKey crypto.PubKey
Commission rational.Rat
CommissionMax int64
CommissionMaxChange int64
Description Description
}
```
### TxEditCandidacy
If either the `Description` (excluding `DateBonded` which is constant),
`Commission`, or the `GovernancePubKey` need to be updated, the
`TxEditCandidacy` transaction should be sent from the owner account:
``` golang
type TxEditCandidacy struct {
GovernancePubKey crypto.PubKey
Commission int64
Description Description
}
```
### TxLivelinessCheck
Liveliness kicks are only checked when a `TxLivelinessCheck` transaction is
submitted.
``` golang
type TxLivelinessCheck struct {
PubKey crypto.PubKey
RewardAccount Addresss
}
```
If the `TxLivelinessCheck is successful in kicking a validator, 5% of the
liveliness punishment is provided as a reward to `RewardAccount`.
### TxProveLive
If the validator was kicked for liveliness issues and is able to regain
liveliness then all delegators in the temporary unbonding pool which have not
transacted to move will be bonded back to the now-live validator and begin to
once again collect provisions and rewards. Regaining livliness is demonstrated
by sending in a `TxProveLive` transaction:
``` golang
type TxProveLive struct {
PubKey crypto.PubKey
}
```
### TxDelegate
All bonding, whether self-bonding or delegation, is done via
`TxDelegate`.
Delegator bonds are created using the TxDelegate transaction. Within this
transaction the validator candidate queried with an amount of coins, whereby
given the current exchange rate of candidate's delegator-shares-to-atoms the
candidate will return shares which are assigned in `DelegatorBond.Shares`.
``` golang
type TxDelegate struct {
PubKey crypto.PubKey
Amount coin.Coin
}
```
### TxUnbond
In this context `TxUnbond` is used to
unbond either delegation bonds or validator self-bonds.
Delegator unbonding is defined by the following transaction type:
``` golang
type TxUnbond struct {
PubKey crypto.PubKey
Shares rational.Rat
}
```
### TxRedelegate
The re-delegation command allows delegators to switch validators while still
receiving equal reward to as if you had never unbonded.
``` golang
type TxRedelegate struct {
PubKeyFrom crypto.PubKey
PubKeyTo crypto.PubKey
Shares rational.Rat
}
```
A delegator who is in the process of unbonding from a validator may use the
re-delegate transaction to bond back to the original validator they're
currently unbonding from (and only that validator). If initiated, the delegator
will immediately begin to one again collect rewards from their validator.
### TxWithdraw
....
## EndBlock
### Update Validators
The validator set is updated in the first block of every hour. Validators are
taken as the first `GlobalState.MaxValidators` number of candidates with the
greatest amount of staked atoms who have not been kicked from the validator
set.
Unbonding of an entire validator-candidate to a temporary liquid account occurs
under the scenarios:
- not enough stake to be within the validator set
- the owner unbonds all of their staked tokens
- validator liveliness issues
- crosses a self-imposed safety threshold
- minimum number of tokens staked by owner
- minimum ratio of tokens staked by owner to delegator tokens
When this occurs delegator's tokens do not unbond to their personal wallets but
begin the unbonding process to a pool where they must then transact in order to
withdraw to their respective wallets.
### Unbonding
When unbonding is initiated, delegator shares are immediately removed from the
candidate and added to a queue object.
In the unbonding queue - the fraction of all historical slashings on
that validator are recorded (`StartSlashRatio`). When this queue reaches maturity
if that total slashing applied is greater on the validator then the
difference (amount that should have been slashed from the first validator) is
assigned to the amount being paid out.
#### Liveliness issues
Liveliness issues are calculated by keeping track of the block precommits in
the block header. A queue is persisted which contains the block headers from
all recent blocks for the duration of the unbonding period.
A validator is defined as having livliness issues if they have not been included in more than
33% of the blocks over:
- The most recent 24 Hours if they have >= 20% of global stake
- The most recent week if they have = 0% of global stake
- Linear interpolation of the above two scenarios
## Invariants
-----------------------------
------------
If a delegator chooses to initiate an unbond or re-delegation of their shares
while a candidate-unbond is commencing, then that unbond/re-delegation is
subject to a reduced unbonding period based on how much time those funds have
already spent in the unbonding queue.
### Re-Delegation
When re-delegation is initiated, delegator shares remain accounted for within
the `Candidate.Shares`, the term `RedelegatingShares` is incremented and a
queue element is created.
During the unbonding period all unbonding shares do not count towards the
voting power of a validator. Once the `QueueElemReDelegation` has reached
maturity, the appropriate unbonding shares are removed from the `Shares` and
`RedelegatingShares` term.
Note that with the current menchanism a delegator cannot redelegate funds which
are currently redelegating.
----------------------------------------------
## Provision Calculations
Every hour atom provisions are assigned proportionally to the each slashable
bonded token which includes re-delegating atoms but not unbonding tokens.
Validation provisions are payed directly to a global hold account
(`BondedTokenPool`) and proportions of that hold account owned by each
validator is defined as the `GlobalStakeBonded`. The tokens are payed as bonded
tokens.
Here, the bonded tokens that a candidate has can be calculated as:
```
globalStakeExRate = params.BondedTokenPool / params.IssuedGlobalStakeShares
candidateCoins = candidate.GlobalStakeShares * globalStakeExRate
```
If a delegator chooses to add more tokens to a validator then the amount of
validator shares distributed is calculated on exchange rate (aka every
delegators shares do not change value at that moment. The validator's
accounting of distributed shares to delegators must also increased at every
deposit.
```
delegatorExRate = validatorCoins / candidate.IssuedDelegatorShares
createShares = coinsDeposited / delegatorExRate
candidate.IssuedDelegatorShares += createShares
```
Whenever a validator has new tokens added to it, the `BondedTokenPool` is
increased and must be reflected in the global parameter as well as the
validators `GlobalStakeShares`. This calculation ensures that the worth of the
`GlobalStakeShares` of other validators remains worth a constant absolute
amount of the `BondedTokenPool`
```
createdGlobalStakeShares = coinsDeposited / globalStakeExRate
validator.GlobalStakeShares += createdGlobalStakeShares
params.IssuedGlobalStakeShares += createdGlobalStakeShares
params.BondedTokenPool += coinsDeposited
```
Similarly, if a delegator wanted to unbond coins:
```
coinsWithdrawn = withdrawlShares * delegatorExRate
destroyedGlobalStakeShares = coinsWithdrawn / globalStakeExRate
validator.GlobalStakeShares -= destroyedGlobalStakeShares
params.IssuedGlobalStakeShares -= destroyedGlobalStakeShares
params.BondedTokenPool -= coinsWithdrawn
```
Note that when an re-delegation occurs the shares to move are placed in an
re-delegation queue where they continue to collect validator provisions until
queue element matures. Although provisions are collected during re-delegation,
re-delegation tokens do not contribute to the voting power of a validator.
Validator provisions are minted on an hourly basis (the first block of a new
hour). The annual target of between 7% and 20%. The long-term target ratio of
bonded tokens to unbonded tokens is 67%.
The target annual inflation rate is recalculated for each previsions cycle. The
inflation is also subject to a rate change (positive of negative) depending or
the distance from the desired ratio (67%). The maximum rate change possible is
defined to be 13% per year, however the annual inflation is capped as between
7% and 20%.
```
inflationRateChange(0) = 0
annualInflation(0) = 0.07
bondedRatio = bondedTokenPool / totalTokenSupply
AnnualInflationRateChange = (1 - bondedRatio / 0.67) * 0.13
annualInflation += AnnualInflationRateChange
if annualInflation > 0.20 then annualInflation = 0.20
if annualInflation < 0.07 then annualInflation = 0.07
provisionTokensHourly = totalTokenSupply * annualInflation / (365.25*24)
```
Because the validators hold a relative bonded share (`GlobalStakeShare`), when
more bonded tokens are added proportionally to all validators the only term
which needs to be updated is the `BondedTokenPool`. So for each previsions
cycle:
```
params.BondedTokenPool += provisionTokensHourly
```
## Fee Calculations
Collected fees are pooled globally and divided out passively to validators and
delegators. Each validator has the opportunity to charge commission to the
delegators on the fees collected on behalf of the delegators by the validators.
Fees are paid directly into a global fee pool. Due to the nature of of passive
accounting whenever changes to parameters which affect the rate of fee
distribution occurs, withdrawal of fees must also occur.
- when withdrawing one must withdrawal the maximum amount they are entitled
too, leaving nothing in the pool,
- when bonding, unbonding, or re-delegating tokens to an existing account a
full withdrawal of the fees must occur (as the rules for lazy accounting
change),
- when a candidate chooses to change the commission on fees, all accumulated
commission fees must be simultaneously withdrawn.
When the validator is the proposer of the round, that validator (and their
delegators) receives between 1% and 5% of fee rewards, the reserve tax is then
charged, then the remainder is distributed socially by voting power to all
validators including the proposer validator. The amount of proposer reward is
calculated from pre-commits Tendermint messages. All provision rewards are
added to a provision reward pool which validator holds individually. Here note
that `BondedShares` represents the sum of all voting power saved in the
`GlobalState` (denoted `gs`).
```
proposerReward = feesCollected * (0.01 + 0.04
* sumOfVotingPowerOfPrecommitValidators / gs.BondedShares)
candidate.ProposerRewardPool += proposerReward
reserveTaxed = feesCollected * params.ReserveTax
gs.ReservePool += reserveTaxed
distributedReward = feesCollected - proposerReward - reserveTaxed
gs.FeePool += distributedReward
gs.SumFeesReceived += distributedReward
gs.RecentFee = distributedReward
```
The entitlement to the fee pool held by the each validator can be accounted for
lazily. First we must account for a candidate's `count` and `adjustment`. The
`count` represents a lazy accounting of what that candidates entitlement to the
fee pool would be if there `VotingPower` was to never change and they were to
never withdraw fees.
```
candidate.count = candidate.VotingPower * BlockHeight
```
Similarly the GlobalState count can be passively calculated whenever needed,
where `BondedShares` is the updated sum of voting powers from all validators.
```
gs.count = gs.BondedShares * BlockHeight
```
The `adjustment` term accounts for changes in voting power and withdrawals of
fees. The adjustment factor must be persisted with the candidate and modified
whenever fees are withdrawn from the candidate or the voting power of the
candidate changes. When the voting power of the candidate changes the
`Adjustment` factor is increased/decreased by the cumulative difference in the
voting power if the voting power has been the new voting power as opposed to
the old voting power for the entire duration of the blockchain up the previous
block. Each time there is an adjustment change the GlobalState (denoted `gs`)
`Adjustment` must also be updated.
```
simplePool = candidate.count / gs.count * gs.SumFeesReceived
projectedPool = candidate.PrevPower * (height-1)
/ (gs.PrevPower * (height-1)) * gs.PrevFeesReceived
+ candidate.Power / gs.Power * gs.RecentFee
AdjustmentChange = simplePool - projectedPool
candidate.AdjustmentRewardPool += AdjustmentChange
gs.Adjustment += AdjustmentChange
```
Every instance that the voting power changes, information about the state of
the validator set during the change must be recorded as a `powerChange` for
other validators to run through. Before any validator modifies its voting power
it must first run through the above calculation to determine the change in
their `caandidate.AdjustmentRewardPool` for all historical changes in the set
of `powerChange` which they have not yet synced to. The set of all
`powerChange` may be trimmed from its oldest members once all validators have
synced past the height of the oldest `powerChange`. This trim procedure will
occur on an epoch basis.
```golang
type powerChange struct {
height int64 // block height at change
power rational.Rat // total power at change
prevpower rational.Rat // total power at previous height-1
feesin coins.Coin // fees in at block height
prevFeePool coins.Coin // total fees in at previous block height
}
```
Note that the adjustment factor may result as negative if the voting power of a
different candidate has decreased.
```
candidate.AdjustmentRewardPool += withdrawn
gs.Adjustment += withdrawn
```
Now the entitled fee pool of each candidate can be lazily accounted for at
any given block:
```
candidate.feePool = candidate.simplePool - candidate.Adjustment
```
So far we have covered two sources fees which can be withdrawn from: Fees from
proposer rewards (`candidate.ProposerRewardPool`), and fees from the fee pool
(`candidate.feePool`). However we should note that all fees from fee pool are
subject to commission rate from the owner of the candidate. These next
calculations outline the math behind withdrawing fee rewards as either a
delegator to a candidate providing commission, or as the owner of a candidate
who is receiving commission.
### Calculations For Delegators and Candidates
The same mechanism described to calculate the fees which an entire validator is
entitled to is be applied to delegator level to determine the entitled fees for
each delegator and the candidates entitled commission from `gs.FeesPool` and
`candidate.ProposerRewardPool`.
The calculations are identical with a few modifications to the parameters:
- Delegator's entitlement to `gs.FeePool`:
- entitled party voting power should be taken as the effective voting power
after commission is retrieved,
`bond.Shares/candidate.TotalDelegatorShares * candidate.VotingPower * (1 - candidate.Commission)`
- Delegator's entitlement to `candidate.ProposerFeePool`
- global power in this context is actually shares
`candidate.TotalDelegatorShares`
- entitled party voting power should be taken as the effective shares after
commission is retrieved, `bond.Shares * (1 - candidate.Commission)`
- Candidate's commission entitlement to `gs.FeePool`
- entitled party voting power should be taken as the effective voting power
of commission portion of total voting power,
`candidate.VotingPower * candidate.Commission`
- Candidate's commission entitlement to `candidate.ProposerFeePool`
- global power in this context is actually shares
`candidate.TotalDelegatorShares`
- entitled party voting power should be taken as the of commission portion
of total delegators shares,
`candidate.TotalDelegatorShares * candidate.Commission`
For more implementation ideas see spreadsheet `spec/AbsoluteFeeDistrModel.xlsx`
As mentioned earlier, every time the voting power of a delegator bond is
changing either by unbonding or further bonding, all fees must be
simultaneously withdrawn. Similarly if the validator changes the commission
rate, all commission on fees must be simultaneously withdrawn.
### Other general notes on fees accounting
- When a delegator chooses to re-delegate shares, fees continue to accumulate
until the re-delegation queue reaches maturity. At the block which the queue
reaches maturity and shares are re-delegated all available fees are
simultaneously withdrawn.
- Whenever a totally new validator is added to the validator set, the `accum`
of the entire candidate must be 0, meaning that the initial value for
`candidate.Adjustment` must be set to the value of `canidate.Count` for the
height which the candidate is added on the validator set.
- The feePool of a new delegator bond will be 0 for the height at which the bond
was added. This is achieved by setting `DelegatorBond.FeeWithdrawalHeight` to
the height which the bond was added.
+94
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