Merge PR #4463: remove docs/_attic

This commit is contained in:
Hans Schoenburg 2019-05-31 14:19:30 -07:00 committed by Alexander Bezobchuk
parent 6ec9b26cef
commit 52be449be1
28 changed files with 0 additions and 3745 deletions

View File

@ -1,2 +0,0 @@
Please note that this folder is a WIP specification for an advanced fee distribution
mechanism which is not set to be implemented.

View File

@ -1,36 +0,0 @@
# End Block
At each endblock, the fees received are sorted to the proposer, community fund,
and global pool. 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 proportionally
by voting power to all bonded validators independent of whether they voted
(social distribution). Note the social distribution is applied to proposer
validator in addition to the proposer reward.
The amount of proposer reward is calculated from pre-commits Tendermint
messages in order to incentivize validators to wait and include additional
pre-commits in the block. All provision rewards are added to a provision reward
pool which validator holds individually
(`ValidatorDistribution.ProvisionsRewardPool`).
```
func SortFees(feesCollected sdk.Coins, global Global, proposer ValidatorDistribution,
sumPowerPrecommitValidators, totalBondedTokens, communityTax sdk.Dec)
feesCollectedDec = MakeDecCoins(feesCollected)
proposerReward = feesCollectedDec * (0.01 + 0.04
* sumPowerPrecommitValidators / totalBondedTokens)
proposer.ProposerPool += proposerReward
communityFunding = feesCollectedDec * communityTax
global.CommunityFund += communityFunding
poolReceived = feesCollectedDec - proposerReward - communityFunding
global.Pool += poolReceived
global.EverReceivedPool += poolReceived
global.LastReceivedPool = poolReceived
SetValidatorDistribution(proposer)
SetGlobal(global)
```

View File

@ -1,16 +0,0 @@
## Future Improvements
### Power Change
Within the current implementation all power changes ever made are indefinitely stored
within the current state. In the future this state should be trimmed on an epoch basis. Delegators
which will have not withdrawn their fees will be penalized in some way, depending on what is
computationally feasible this may include:
- burning non-withdrawn fees
- requiring more expensive withdrawal costs which include proofs from archive nodes of historical state
In addition or as an alternative it may make sense to implement a "rolling" epoch which cycles through
all the delegators in small groups (for example 5 delegators per block) and just runs the withdrawal transaction
at standard rates and takes transaction fees from the withdrawal amount.

View File

@ -1,54 +0,0 @@
# Distribution
## Overview
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, and validator proposer-reward
pool. Due to the nature 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,
- 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),
- a validator chooses to change the commission on fees, all accumulated
commission fees must be simultaneously withdrawn.
The above scenarios are covered in `triggers.md`.
The distribution mechanism outlines herein is used to lazily distribute the
following between validators and associated delegators:
- multi-token fees to be socially distributed,
- proposer reward pool,
- inflated atom provisions, and
- validator commission on all rewards earned by their delegators stake
Fees are pooled within a global pool, as well as validator specific
proposer-reward pools. The mechanisms used allow for validators and delegators
to independently and lazily withdrawn their rewards. As a part of the lazy
computations adjustment factors must be maintained for each validator and
delegator to determine the true proportion of fees in each pool which 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 change their portion of bonded
Atoms.
## Affect on Staking
Charging commission on Atom provisions while also allowing for Atom-provisions
to be auto-bonded (distributed directly to the validators bonded stake) is
problematic within DPoS. Fundamentally these two mechnisms are mutually
exclusive. If there are atoms commissions and auto-bonding Atoms, the portion
of Atoms the fee distribution calculation would become very large as the Atom
portion for each delegator would change each block making a withdrawal of fees
for a delegator require a calculation for every single block since the last
withdrawal. In conclusion we can only have atom commission and unbonded atoms
provisions, or bonded atom provisions with no Atom commission, and we elect to
implement the former. Stakeholders wishing to rebond their provisions may elect
to set up a script to periodically withdraw and rebond fees.

View File

@ -1,100 +0,0 @@
## State
### Global
All globally tracked parameters for distribution are stored within
`Global`. Rewards are collected and added to the reward pool and
distributed to validators/delegators from here.
Note that the reward pool holds decimal coins (`DecCoins`) to allow
for fractions of coins to be received from operations like inflation.
When coins are distributed from the pool they are truncated back to
`sdk.Coins` which are non-decimal.
- Global: `0x00 -> amino(global)`
```golang
// coins with decimal
type DecCoins []DecCoin
type DecCoin struct {
Amount sdk.Dec
Denom string
}
type Global struct {
PrevBondedTokens sdk.Dec // bonded token amount for the global pool on the previous block
Adjustment sdk.Dec // global adjustment factor for lazy calculations
Pool DecCoins // funds for all validators which have yet to be withdrawn
PrevReceivedPool DecCoins // funds added to the pool on the previous block
EverReceivedPool DecCoins // total funds ever added to the pool
CommunityFund DecCoins // pool for community funds yet to be spent
}
```
### Validator Distribution
Validator distribution information for the relevant validator is updated each time:
1. delegation amount to a validator are updated,
2. a validator successfully proposes a block and receives a reward,
3. any delegator withdraws from a validator, or
4. the validator withdraws it's commission.
- ValidatorDistribution: `0x02 | ValOwnerAddr -> amino(validatorDistribution)`
```golang
type ValidatorDistribution struct {
CommissionWithdrawalHeight int64 // last time this validator withdrew commission
Adjustment sdk.Dec // global pool adjustment factor
ProposerAdjustment DecCoins // proposer pool adjustment factor
ProposerPool DecCoins // reward pool collected from being the proposer
EverReceivedProposerReward DecCoins // all rewards ever collected from being the proposer
PrevReceivedProposerReward DecCoins // previous rewards collected from being the proposer
PrevBondedTokens sdk.Dec // bonded token amount on the previous block
PrevDelegatorShares sdk.Dec // amount of delegator shares for the validator on the previous block
}
```
### Delegation Distribution
Each delegation holds multiple adjustment factors to specify its entitlement to
the rewards from a validator. `AdjustmentPool` is used to passively calculate
each bonds entitled fees from the `RewardPool`. `AdjustmentPool` is used to
passively calculate each bonds entitled fees from
`ValidatorDistribution.ProposerRewardPool`
- DelegatorDistribution: ` 0x02 | DelegatorAddr | ValOwnerAddr -> amino(delegatorDist)`
```golang
type DelegatorDist struct {
WithdrawalHeight int64 // last time this delegation withdrew rewards
Adjustment sdk.Dec // fee provisioning adjustment factor
AdjustmentProposer DecCoins // proposers pool adjustment factor
PrevTokens sdk.Dec // bonded tokens held by the delegation on the previous block
PrevShares sdk.Dec // delegator shares held by the delegation on the previous block
}
```
### Power Change
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. Each power change is indexed by its block
height.
- PowerChange: `0x03 | amino(Height) -> amino(validatorDist)`
```golang
type PowerChange struct {
Height int64 // block height at change
ValidatorBondedTokens sdk.Dec // following used to create distribution scenarios
ValidatorDelegatorShares sdk.Dec
ValidatorDelegatorShareExRate sdk.Dec
ValidatorCommission sdk.Dec
PoolBondedTokens sdk.Dec
Global Global
ValDistr ValidatorDistribution
DelegationShares sdk.Dec
DelDistr DelegatorDistribution
}
```

View File

@ -1,399 +0,0 @@
# Transactions
## TxWithdrawDelegation
When a delegator wishes to withdraw their transaction fees it must send
`TxWithdrawDelegation`. Note that parts of this transaction logic are also
triggered each with any change in individual delegations, such as an unbond,
redelegation, or delegation of additional tokens to a specific validator.
Each time a withdrawal is made by a recipient the adjustment term must be
modified for each block with a change in distributors shares since the time of
last withdrawal. This is accomplished by iterating over all relevant
`PowerChange`'s stored in distribution state.
```golang
type TxWithdrawDelegation struct {
delegatorAddr sdk.AccAddress
withdrawAddr sdk.AccAddress // address to make the withdrawal to
}
func WithdrawDelegator(delegatorAddr, withdrawAddr sdk.AccAddress)
entitlement = GetDelegatorEntitlement(delegatorAddr)
AddCoins(withdrawAddr, totalEntitlment.TruncateDecimal())
func GetDelegatorEntitlement(delegatorAddr sdk.AccAddress) DecCoins
// compile all the distribution scenarios
delegations = GetDelegations(delegatorAddr)
DelDistr = GetDelegationDistribution(delegation.DelegatorAddr,
delegation.ValidatorAddr)
pcs = GetPowerChanges(DelDistr.WithdrawalHeight)
// update all adjustment factors for each delegation since last withdrawal
for pc = range pcs
for delegation = range delegations
DelDistr = GetDelegationDistribution(delegation.DelegatorAddr,
delegation.ValidatorAddr)
pc.ProcessPowerChangeDelegation(delegation, DelDistr)
// collect all entitled fees
entitlement = 0
for delegation = range delegations
global = GetGlobal()
pool = GetPool()
DelDistr = GetDelegationDistribution(delegation.DelegatorAddr,
delegation.ValidatorAddr)
ValDistr = GetValidatorDistribution(delegation.ValidatorAddr)
validator = GetValidator(delegation.ValidatorAddr)
scenerio1 = NewDelegationFromGlobalPool(delegation, validator,
pool, global, ValDistr, DelDistr)
scenerio2 = NewDelegationFromProvisionPool(delegation, validator,
ValDistr, DelDistr)
entitlement += scenerio1.WithdrawalEntitlement()
entitlement += scenerio2.WithdrawalEntitlement()
return entitlement
func (pc PowerChange) ProcessPowerChangeDelegation(delegation sdk.Delegation,
DelDistr DelegationDistribution)
// get the historical scenarios
scenario1 = pc.DelegationFromGlobalPool(delegation, DelDistr)
scenario2 = pc.DelegationFromProvisionPool(delegation, DelDistr)
// process the adjustment factors
scenario1.UpdateAdjustmentForPowerChange(pc.Height)
scenario2.UpdateAdjustmentForPowerChange(pc.Height)
```
## TxWithdrawValidator
When a validator wishes to withdraw their transaction fees it must send
`TxWithdrawDelegation`. Note that parts of this transaction logic is also
triggered each with any change in individual delegations, such as an unbond,
redelegation, or delegation of additional tokens to a specific validator. This
transaction withdraws the validators commission rewards, as well as any rewards
earning on their self-delegation.
```golang
type TxWithdrawValidator struct {
ownerAddr sdk.AccAddress // validator address to withdraw from
withdrawAddr sdk.AccAddress // address to make the withdrawal to
}
func WithdrawalValidator(ownerAddr, withdrawAddr sdk.AccAddress)
// update the delegator adjustment factors and also withdrawal delegation fees
entitlement = GetDelegatorEntitlement(ownerAddr)
// update the validator adjustment factors for commission
ValDistr = GetValidatorDistribution(ownerAddr.ValidatorAddr)
pcs = GetPowerChanges(ValDistr.CommissionWithdrawalHeight)
for pc = range pcs
pc.ProcessPowerChangeCommission()
// withdrawal validator commission rewards
global = GetGlobal()
pool = GetPool()
ValDistr = GetValidatorDistribution(delegation.ValidatorAddr)
validator = GetValidator(delegation.ValidatorAddr)
scenerio1 = NewCommissionFromGlobalPool(validator,
pool, global, ValDistr)
scenerio2 = CommissionFromProposerPool(validator, ValDistr)
entitlement += scenerio1.WithdrawalEntitlement()
entitlement += scenerio2.WithdrawalEntitlement()
AddCoins(withdrawAddr, totalEntitlment.TruncateDecimal())
func (pc PowerChange) ProcessPowerChangeCommission()
// get the historical scenarios
scenario1 = pc.CommissionFromGlobalPool()
scenario2 = pc.CommissionFromProposerPool()
// process the adjustment factors
scenario1.UpdateAdjustmentForPowerChange(pc.Height)
scenario2.UpdateAdjustmentForPowerChange(pc.Height)
```
## Common Calculations
### Distribution scenario
A common form of abstracted calculations exists between validators and
delegations attempting to withdrawal their rewards, either from `Global.Pool`
or from `ValidatorDistribution.ProposerPool`. With the following interface
fulfilled the entitled fees for the various scenarios can be calculated.
```golang
type DistributionScenario interface {
DistributorTokens() DecCoins // current tokens from distributor
DistributorCumulativeTokens() DecCoins // total tokens ever received
DistributorPrevReceivedTokens() DecCoins // last value of tokens received
DistributorShares() sdk.Dec // current shares
DistributorPrevShares() sdk.Dec // shares last block
RecipientAdjustment() sdk.Dec
RecipientShares() sdk.Dec // current shares
RecipientPrevShares() sdk.Dec // shares last block
ModifyAdjustments(withdrawal sdk.Dec) // proceedure to modify adjustment factors
}
```
#### Entitled reward from distribution scenario
The entitlement to the distributor's tokens held can be accounted for lazily.
To begin this calculation we must determine the recipient's _simple pool_ and
_projected pool_. The simple pool represents a lazy accounting of what a
recipient's entitlement to the distributor's tokens would be if all recipients
for that distributor had static shares (equal to the current shares), and no
recipients had ever withdrawn their entitled rewards. The projected pool
represents the anticipated recipient's entitlement to the distributors tokens
based on the current blocks token input (for example fees reward received) to
the distributor, and the distributor's tokens and shares of the previous block
assuming that neither had changed in the current block. Using the simple and
projected pools we can determine all cumulative changes which have taken place
outside of the recipient and adjust the recipient's _adjustment factor_ to
account for these changes and ultimately keep track of the correct entitlement
to the distributors tokens.
```
func (d DistributionScenario) RecipientCount(height int64) sdk.Dec
return v.RecipientShares() * height
func (d DistributionScenario) GlobalCount(height int64) sdk.Dec
return d.DistributorShares() * height
func (d DistributionScenario) SimplePool() DecCoins
return d.RecipientCount() / d.GlobalCount() * d.DistributorCumulativeTokens
func (d DistributionScenario) ProjectedPool(height int64) DecCoins
return d.RecipientPrevShares() * (height-1)
/ (d.DistributorPrevShares() * (height-1))
* d.DistributorCumulativeTokens
+ d.RecipientShares() / d.DistributorShares()
* d.DistributorPrevReceivedTokens()
```
The `DistributionScenario` _adjustment_ terms account for changes in
recipient/distributor shares and recipient withdrawals. The adjustment factor
must be modified whenever the recipient withdraws from the distributor or the
distributor's/recipient's shares are changed.
- When the shares of the recipient is changed the adjustment factor is
increased/decreased by the difference between the _simple_ and _projected_
pools. In other words, the cumulative difference in the shares if the shares
has been the new shares as opposed to the old shares for the entire duration of
the blockchain up the previous block.
- When a recipient makes a withdrawal the adjustment factor is increased by the
withdrawal amount.
```
func (d DistributionScenario) UpdateAdjustmentForPowerChange(height int64)
simplePool = d.SimplePool()
projectedPool = d.ProjectedPool(height)
AdjustmentChange = simplePool - projectedPool
if AdjustmentChange > 0
d.ModifyAdjustments(AdjustmentChange)
func (d DistributionScenario) WithdrawalEntitlement() DecCoins
entitlement = d.SimplePool() - d.RecipientAdjustment()
d.ModifyAdjustments(entitlement)
return entitlement
```
### Distribution scenarios
Note that the distribution scenario structures are found in `state.md`.
#### Delegation's entitlement to Global.Pool
For delegations (including validator's self-delegation) all fees from fee pool
are subject to commission rate from the operator of the validator. The global
shares should be taken as true number of global bonded shares. The recipients
shares should be taken as the bonded tokens less the validator's commission.
```
type DelegationFromGlobalPool struct {
DelegationShares sdk.Dec
ValidatorCommission sdk.Dec
ValidatorBondedTokens sdk.Dec
ValidatorDelegatorShareExRate sdk.Dec
PoolBondedTokens sdk.Dec
Global Global
ValDistr ValidatorDistribution
DelDistr DelegatorDistribution
}
func (d DelegationFromGlobalPool) DistributorTokens() DecCoins
return d.Global.Pool
func (d DelegationFromGlobalPool) DistributorCumulativeTokens() DecCoins
return d.Global.EverReceivedPool
func (d DelegationFromGlobalPool) DistributorPrevReceivedTokens() DecCoins
return d.Global.PrevReceivedPool
func (d DelegationFromGlobalPool) DistributorShares() sdk.Dec
return d.PoolBondedTokens
func (d DelegationFromGlobalPool) DistributorPrevShares() sdk.Dec
return d.Global.PrevBondedTokens
func (d DelegationFromGlobalPool) RecipientShares() sdk.Dec
return d.DelegationShares * d.ValidatorDelegatorShareExRate() *
d.ValidatorBondedTokens() * (1 - d.ValidatorCommission)
func (d DelegationFromGlobalPool) RecipientPrevShares() sdk.Dec
return d.DelDistr.PrevTokens
func (d DelegationFromGlobalPool) RecipientAdjustment() sdk.Dec
return d.DelDistr.Adjustment
func (d DelegationFromGlobalPool) ModifyAdjustments(withdrawal sdk.Dec)
d.ValDistr.Adjustment += withdrawal
d.DelDistr.Adjustment += withdrawal
d.global.Adjustment += withdrawal
SetValidatorDistribution(d.ValDistr)
SetDelegatorDistribution(d.DelDistr)
SetGlobal(d.Global)
```
#### Delegation's entitlement to ValidatorDistribution.ProposerPool
Delegations (including validator's self-delegation) are still subject
commission on the rewards gained from the proposer pool. Global shares in this
context is actually the validators total delegations shares. The recipient's
shares is taken as the effective delegation shares less the validator's
commission.
```
type DelegationFromProposerPool struct {
DelegationShares sdk.Dec
ValidatorCommission sdk.Dec
ValidatorDelegatorShares sdk.Dec
ValDistr ValidatorDistribution
DelDistr DelegatorDistribution
}
func (d DelegationFromProposerPool) DistributorTokens() DecCoins
return d.ValDistr.ProposerPool
func (d DelegationFromProposerPool) DistributorCumulativeTokens() DecCoins
return d.ValDistr.EverReceivedProposerReward
func (d DelegationFromProposerPool) DistributorPrevReceivedTokens() DecCoins
return d.ValDistr.PrevReceivedProposerReward
func (d DelegationFromProposerPool) DistributorShares() sdk.Dec
return d.ValidatorDelegatorShares
func (d DelegationFromProposerPool) DistributorPrevShares() sdk.Dec
return d.ValDistr.PrevDelegatorShares
func (d DelegationFromProposerPool) RecipientShares() sdk.Dec
return d.DelegationShares * (1 - d.ValidatorCommission)
func (d DelegationFromProposerPool) RecipientPrevShares() sdk.Dec
return d.DelDistr.PrevShares
func (d DelegationFromProposerPool) RecipientAdjustment() sdk.Dec
return d.DelDistr.AdjustmentProposer
func (d DelegationFromProposerPool) ModifyAdjustments(withdrawal sdk.Dec)
d.ValDistr.AdjustmentProposer += withdrawal
d.DelDistr.AdjustmentProposer += withdrawal
SetValidatorDistribution(d.ValDistr)
SetDelegatorDistribution(d.DelDistr)
```
#### Validators's commission entitlement to Global.Pool
Similar to a delegator's entitlement, but with recipient shares based on the
commission portion of bonded tokens.
```
type CommissionFromGlobalPool struct {
ValidatorBondedTokens sdk.Dec
ValidatorCommission sdk.Dec
PoolBondedTokens sdk.Dec
Global Global
ValDistr ValidatorDistribution
}
func (c CommissionFromGlobalPool) DistributorTokens() DecCoins
return c.Global.Pool
func (c CommissionFromGlobalPool) DistributorCumulativeTokens() DecCoins
return c.Global.EverReceivedPool
func (c CommissionFromGlobalPool) DistributorPrevReceivedTokens() DecCoins
return c.Global.PrevReceivedPool
func (c CommissionFromGlobalPool) DistributorShares() sdk.Dec
return c.PoolBondedTokens
func (c CommissionFromGlobalPool) DistributorPrevShares() sdk.Dec
return c.Global.PrevBondedTokens
func (c CommissionFromGlobalPool) RecipientShares() sdk.Dec
return c.ValidatorBondedTokens() * c.ValidatorCommission
func (c CommissionFromGlobalPool) RecipientPrevShares() sdk.Dec
return c.ValDistr.PrevBondedTokens * c.ValidatorCommission
func (c CommissionFromGlobalPool) RecipientAdjustment() sdk.Dec
return c.ValDistr.Adjustment
func (c CommissionFromGlobalPool) ModifyAdjustments(withdrawal sdk.Dec)
c.ValDistr.Adjustment += withdrawal
c.Global.Adjustment += withdrawal
SetValidatorDistribution(c.ValDistr)
SetGlobal(c.Global)
```
#### Validators's commission entitlement to ValidatorDistribution.ProposerPool
Similar to a delegators entitlement to the proposer pool, but with recipient
shares based on the commission portion of the total delegator shares.
```
type CommissionFromProposerPool struct {
ValidatorDelegatorShares sdk.Dec
ValidatorCommission sdk.Dec
ValDistr ValidatorDistribution
}
func (c CommissionFromProposerPool) DistributorTokens() DecCoins
return c.ValDistr.ProposerPool
func (c CommissionFromProposerPool) DistributorCumulativeTokens() DecCoins
return c.ValDistr.EverReceivedProposerReward
func (c CommissionFromProposerPool) DistributorPrevReceivedTokens() DecCoins
return c.ValDistr.PrevReceivedProposerReward
func (c CommissionFromProposerPool) DistributorShares() sdk.Dec
return c.ValidatorDelegatorShares
func (c CommissionFromProposerPool) DistributorPrevShares() sdk.Dec
return c.ValDistr.PrevDelegatorShares
func (c CommissionFromProposerPool) RecipientShares() sdk.Dec
return c.ValidatorDelegatorShares * (c.ValidatorCommission)
func (c CommissionFromProposerPool) RecipientPrevShares() sdk.Dec
return c.ValDistr.PrevDelegatorShares * (c.ValidatorCommission)
func (c CommissionFromProposerPool) RecipientAdjustment() sdk.Dec
return c.ValDistr.AdjustmentProposer
func (c CommissionFromProposerPool) ModifyAdjustments(withdrawal sdk.Dec)
c.ValDistr.AdjustmentProposer += withdrawal
SetValidatorDistribution(c.ValDistr)
```

View File

@ -1,31 +0,0 @@
# Triggers
## Create validator distribution
- triggered-by: validator entering bonded validator group (`stake.bondValidator()`)
Whenever a new validator is added to the Tendermint validator set they are
entitled to begin earning rewards of atom provisions and fees. At this point
`ValidatorDistribution.Pool()` must be zero (as the validator has not yet
earned any rewards) meaning that the initial value for `validator.Adjustment`
must be set to the value of `validator.SimplePool()` for the height which the
validator is added on the validator set.
## Create or modify delegation distribution
- triggered-by: `stake.TxDelegate`, `stake.TxBeginRedelegate`, `stake.TxBeginUnbonding`
The pool of a new delegator bond will be 0 for the height at which the bond was
added. This is achieved by setting `DelegationDistribution.WithdrawalHeight` to
the height which the bond was added. Additionally the `AdjustmentPool` and
`AdjustmentProposerPool` must be set to the equivalent values of
`DelegationDistribution.SimplePool()` and
`DelegationDistribution.SimpleProposerPool()` for the height of delegation.
## Commission rate change
- triggered-by: `stake.TxEditValidator`
If a validator changes its commission rate, all commission on fees must be
simultaneously withdrawn using the transaction `TxWithdrawValidator`

View File

@ -1,491 +0,0 @@
# 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 Route.
// Must be alphanumeric or empty.
// Must correspond to name of message handler (XXX).
Route() 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
// GetSigners 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() []AccAddress
}
```
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.AccAddress `json:"from"`
To sdk.AccAddress `json:"to"`
Amount sdk.Coins `json:"amount"`
}
// Implements Msg.
func (msg MsgSend) Route() string { return "send" }
```
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.AccAddress {
return []sdk.AccAddress{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 explicit access to a store's key will be able to access that store during message processsing.
### Context
The SDK uses a `Context` to propagate 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 propagate 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/tendermint/blob/master/abci/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.
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.AccAddress, 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.AccAddress, 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/tendermint/tree/master/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/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 {
// Create the base application object.
app := bapp.NewBaseApp(app1Name, logger, db, tx1Decoder)
// Create a capability key for accessing the account store.
keyAccount := sdk.NewKVStoreKey(auth.StoreKey)
// Register message routes.
// Note the handler receives the keyAccount and thus
// gets access to the account store.
app.Router().
AddRoute("send", 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, `send`, 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 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/tendermint/blob/master/docs/app-dev/abci-spec.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!

View File

@ -1,307 +0,0 @@
# Transactions
In the previous app we built a simple bank with one message type `send` 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.AccAddress
Receiver sdk.AccAddress
Coin sdk.Coin
}
// Implements Msg.
func (msg MsgIssue) Route() string { return "issue" }
```
Note the `Route()` method returns `"issue"`, so this message is of a different
route 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.AccAddress, 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.AccAddress `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() *codec.Codec {
cdc := codec.New()
cdc.RegisterInterface((*sdk.Msg)(nil), nil)
cdc.RegisterConcrete(MsgSend{}, "example/MsgSend", nil)
cdc.RegisterConcrete(MsgIssue{}, "example/MsgIssue", nil)
crypto.RegisterAmino(cdc)
return cdc
}
```
Note: We also register the types in the `tendermint/tendermint/crypto` module so that `crypto.PubKey`
is encoded/decoded correctly.
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 []byte
}
// This tx only has one Msg.
func (tx app2Tx) GetMsgs() []sdk.Msg {
return []sdk.Msg{tx.Msg}
}
// Amino decode app2Tx. Capable of decoding both MsgSend and MsgIssue
func tx2Decoder(cdc *codec.Codec) sdk.TxDecoder {
return func(txBytes []byte) (sdk.Tx, sdk.Error) {
var tx app2Tx
err := cdc.UnmarshalBinaryLengthPrefixed(txBytes, &tx)
if err != nil {
return nil, sdk.ErrTxDecode(err.Error())
}
return tx, nil
}
}
```
## 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 and one signer in app2Tx
msg := tx.GetMsgs()[0]
signerAddr := msg.GetSigners()[0]
signBytes := msg.GetSignBytes()
sig := appTx.GetSignature()
// check that submitted pubkey belongs to required address
if !bytes.Equal(appTx.PubKey.Address(), signerAddr) {
return ctx, sdk.ErrUnauthorized("Provided Pubkey does not match required address").Result(), true
}
// check that signature is over expected signBytes
if !appTx.PubKey.VerifyBytes(signBytes, sig) {
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, logger, db, txDecoder(cdc))
// Create a key for accessing the account store.
keyAccount := sdk.NewKVStoreKey(auth.StoreKey)
// 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 now that we're using Amino, we create a codec, register our types on the codec, and pass the
codec into our TxDecoder constructor, `tx2Decoder`. 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.

View File

@ -1,371 +0,0 @@
# 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.AccAddress
SetAddress(sdk.AccAddress) error // errors if already set.
GetPubKey() crypto.PubKey // can return nil.
SetPubKey(crypto.PubKey) error
GetAccountNumber() uint64
SetAccountNumber(uint64) error
GetSequence() uint64
SetSequence(uint64) 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.
type BaseAccount struct {
Address sdk.AccAddress `json:"address"`
Coins sdk.Coins `json:"coins"`
PubKey crypto.PubKey `json:"public_key"`
AccountNumber uint64 `json:"account_number"`
Sequence uint64 `json:"sequence"`
}
```
It simply contains a field for each of the methods.
### AccountKeeper
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 `AccountKeeper` 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 AccountKeeper is easy - we just need to specify a codec, a
capability key, and a prototype of the object being encoded
```go
accountKeeper := auth.NewAccountKeeper(cdc, keyAccount, auth.ProtoBaseAccount)
```
Then we can get, modify, and set accounts. For instance, we could double the
amount of coins in an account:
```go
acc := accountKeeper.GetAccount(ctx, addr)
acc.SetCoins(acc.Coins.Plus(acc.Coins))
accountKeeper.SetAccount(ctx, addr)
```
Note that the `AccountKeeper` 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 [AccountKeeper API
docs](https://godoc.org/github.com/cosmos/cosmos-sdk/x/auth#AccountKeeper) 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 fee payer (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
[]byte `json:"signature"`
AccountNumber uint64 `json:"account_number"`
Sequence uint64 `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 `AccountKeeper` and works with `StdTx`:
```go
app.SetAnteHandler(auth.NewAnteHandler(accountKeeper, 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`.
## CoinKeeper
Now that we've seen the `auth.AccountKeeper` 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 `AccountKeeper` 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 `AccountKeeper`:
```go
bankKeeper = bank.NewBaseKeeper(accountKeeper)
```
We can then use it within a handler, instead of working directly with the
`AccountKeeper`. For instance, to add coins to an account:
```go
// Finds account with addr in AccountKeeper.
// Adds coins to account's coin array.
// Sets updated account in AccountKeeper
app.bankKeeper.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 `AccountKeeper`,
we restrict access to a small number of functions that do very specific things.
## App3
With the `auth.AccountKeeper` 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, logger, db, auth.DefaultTxDecoder(cdc))
// Create a key for accessing the account store.
keyAccount := sdk.NewKVStoreKey(auth.StoreKey)
keyFees := sdk.NewKVStoreKey(auth.FeeStoreKey) // TODO
// Set various mappers/keepers to interact easily with underlying stores
accountKeeper := auth.NewAccountKeeper(cdc, keyAccount, auth.ProtoBaseAccount)
bankKeeper := bank.NewBaseKeeper(accountKeeper)
feeKeeper := auth.NewFeeCollectionKeeper(cdc, keyFees)
app.SetAnteHandler(auth.NewAnteHandler(accountKeeper, feeKeeper))
// Register message routes.
// Note the handler gets access to
app.Router().
AddRoute("send", bank.NewHandler(bankKeeper))
// 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.
We also use the default txDecoder in `x/auth`, which decodes amino-encoded
`auth.StdTx` transactions.
## 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.

View File

@ -1,73 +0,0 @@
# 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/tendermint/blob/master/docs/app-dev/abci-spec.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.

View File

@ -1,72 +0,0 @@
# 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/blob/master/tmlibs/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,233 +0,0 @@
package app
import (
"encoding/json"
cmn "github.com/tendermint/tendermint/libs/common"
dbm "github.com/tendermint/tendermint/libs/db"
"github.com/tendermint/tendermint/libs/log"
bapp "github.com/cosmos/cosmos-sdk/baseapp"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
)
const (
app1Name = "App1"
bankCodespace = "BANK"
)
func NewApp1(logger log.Logger, db dbm.DB) *bapp.BaseApp {
// Create the base application object.
app := bapp.NewBaseApp(app1Name, logger, db, tx1Decoder)
// Create a key for accessing the account store.
keyAccount := sdk.NewKVStoreKey(auth.StoreKey)
// 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.AccAddress `json:"from"`
To sdk.AccAddress `json:"to"`
Amount sdk.Coins `json:"amount"`
}
// NewMsgSend
func NewMsgSend(from, to sdk.AccAddress, amt sdk.Coins) MsgSend {
return MsgSend{from, to, amt}
}
// Implements Msg.
// nolint
func (msg MsgSend) Route() string { return "send" }
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 sdk.MustSortJSON(bz)
}
// Implements Msg. Return the signer.
func (msg MsgSend) GetSigners() []sdk.AccAddress {
return []sdk.AccAddress{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(bankCodespace, 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.AccAddress, 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(bankCodespace, 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.Sub(amt)
// If any coin has negative amount, return insufficient coins error.
if senderCoins.IsAnyNegative() {
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.AccAddress, 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.Add(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 tx1Decoder(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
}

View File

@ -1,244 +0,0 @@
package app
import (
"bytes"
"encoding/json"
"fmt"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/ed25519"
cryptoAmino "github.com/tendermint/tendermint/crypto/encoding/amino"
cmn "github.com/tendermint/tendermint/libs/common"
dbm "github.com/tendermint/tendermint/libs/db"
"github.com/tendermint/tendermint/libs/log"
bapp "github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
)
const (
app2Name = "App2"
)
var (
issuer = ed25519.GenPrivKey().PubKey().Address()
)
func NewCodec() *codec.Codec {
cdc := codec.New()
cdc.RegisterInterface((*sdk.Msg)(nil), nil)
cdc.RegisterConcrete(MsgSend{}, "example/MsgSend", nil)
cdc.RegisterConcrete(MsgIssue{}, "example/MsgIssue", nil)
cryptoAmino.RegisterAmino(cdc)
return cdc
}
func NewApp2(logger log.Logger, db dbm.DB) *bapp.BaseApp {
cdc := NewCodec()
// Create the base application object.
app := bapp.NewBaseApp(app2Name, logger, db, tx2Decoder(cdc))
// Create a key for accessing the account store.
keyAccount := sdk.NewKVStoreKey(auth.StoreKey)
// 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.AccAddress
Receiver sdk.AccAddress
Coin sdk.Coin
}
// Implements Msg.
// nolint
func (msg MsgIssue) Route() string { return "issue" }
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 sdk.MustSortJSON(bz)
}
// Implements Msg. Return the signer.
func (msg MsgIssue) GetSigners() []sdk.AccAddress {
return []sdk.AccAddress{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(bankCodespace, 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.AccAddress, 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.AccAddress `json:"issuer"`
}
//------------------------------------------------------------------
// Tx
// Simple tx to wrap the Msg.
type app2Tx struct {
sdk.Msg
PubKey crypto.PubKey
Signature []byte
}
// This tx only has one Msg.
func (tx app2Tx) GetMsgs() []sdk.Msg {
return []sdk.Msg{tx.Msg}
}
func (tx app2Tx) GetSignature() []byte {
return tx.Signature
}
// Amino decode app2Tx. Capable of decoding both MsgSend and MsgIssue
func tx2Decoder(cdc *codec.Codec) sdk.TxDecoder {
return func(txBytes []byte) (sdk.Tx, sdk.Error) {
var tx app2Tx
err := cdc.UnmarshalBinaryLengthPrefixed(txBytes, &tx)
if err != nil {
return nil, sdk.ErrTxDecode(err.Error())
}
return tx, nil
}
}
//------------------------------------------------------------------
// Simple anteHandler that ensures msg signers have signed.
// Provides no replay protection.
func antehandler(ctx sdk.Context, tx sdk.Tx, simulate bool) (_ 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 and one signer in app2Tx
msg := tx.GetMsgs()[0]
signerAddr := msg.GetSigners()[0]
signBytes := msg.GetSignBytes()
sig := appTx.GetSignature()
// check that submitted pubkey belongs to required address
if !bytes.Equal(appTx.PubKey.Address(), signerAddr) {
return ctx, sdk.ErrUnauthorized("Provided Pubkey does not match required address").Result(), true
}
// check that signature is over expected signBytes
if !appTx.PubKey.VerifyBytes(signBytes, sig) {
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

@ -1,85 +0,0 @@
package app
import (
"testing"
"github.com/tendermint/tendermint/crypto/ed25519"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/require"
)
// Test encoding of app2Tx is correct with both msg types
func TestEncoding(t *testing.T) {
// Create privkeys and addresses
priv1 := ed25519.GenPrivKey()
priv2 := ed25519.GenPrivKey()
addr1 := priv1.PubKey().Address().Bytes()
addr2 := priv2.PubKey().Address().Bytes()
sendMsg := MsgSend{
From: addr1,
To: addr2,
Amount: sdk.Coins{sdk.NewCoin("testcoins", sdk.NewInt(100))},
}
// Construct transaction
signBytes := sendMsg.GetSignBytes()
sig, err := priv1.Sign(signBytes)
if err != nil {
panic(err)
}
sendTxBefore := app2Tx{
Msg: sendMsg,
PubKey: priv1.PubKey(),
Signature: sig,
}
cdc := NewCodec()
testTxDecoder := tx2Decoder(cdc)
encodedSendTx, err := cdc.MarshalBinaryLengthPrefixed(sendTxBefore)
require.Nil(t, err, "Error encoding sendTx")
var tx1 sdk.Tx
tx1, err = testTxDecoder(encodedSendTx)
require.Nil(t, err, "Error decoding sendTx")
sendTxAfter := tx1.(app2Tx)
require.Equal(t, sendTxBefore, sendTxAfter, "Transaction changed after encoding/decoding")
issueMsg := MsgIssue{
Issuer: addr1,
Receiver: addr2,
Coin: sdk.NewCoin("testcoin", sdk.NewInt(100)),
}
signBytes = issueMsg.GetSignBytes()
sig, err = priv1.Sign(signBytes)
if err != nil {
panic(err)
}
issueTxBefore := app2Tx{
Msg: issueMsg,
PubKey: priv1.PubKey(),
Signature: sig,
}
encodedIssueTx, err2 := cdc.MarshalBinaryLengthPrefixed(issueTxBefore)
require.Nil(t, err2, "Error encoding issueTx")
var tx2 sdk.Tx
tx2, err2 = testTxDecoder(encodedIssueTx)
require.Nil(t, err2, "Error decoding issue Tx")
issueTxAfter := tx2.(app2Tx)
require.Equal(t, issueTxBefore, issueTxAfter, "Transaction changed after encoding/decoding")
}

View File

@ -1,62 +0,0 @@
package app
import (
cryptoAmino "github.com/tendermint/tendermint/crypto/encoding/amino"
cmn "github.com/tendermint/tendermint/libs/common"
dbm "github.com/tendermint/tendermint/libs/db"
"github.com/tendermint/tendermint/libs/log"
bapp "github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/codec"
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 := UpdatedCodec()
// Create the base application object.
app := bapp.NewBaseApp(app3Name, logger, db, auth.DefaultTxDecoder(cdc))
// Create a key for accessing the account store.
keyAccount := sdk.NewKVStoreKey(auth.StoreKey)
keyFees := sdk.NewKVStoreKey(auth.FeeStoreKey) // TODO
// Set various mappers/keepers to interact easily with underlying stores
accountKeeper := auth.NewAccountKeeper(cdc, keyAccount, auth.ProtoBaseAccount)
bankKeeper := bank.NewBaseKeeper(accountKeeper)
feeKeeper := auth.NewFeeCollectionKeeper(cdc, keyFees)
app.SetAnteHandler(auth.NewAnteHandler(accountKeeper, feeKeeper))
// Register message routes.
// Note the handler gets access to
app.Router().
AddRoute("bank", bank.NewHandler(bankKeeper))
// Mount stores and load the latest state.
app.MountStoresIAVL(keyAccount, keyFees)
err := app.LoadLatestVersion(keyAccount)
if err != nil {
cmn.Exit(err.Error())
}
return app
}
// Update codec from app2 to register imported modules
func UpdatedCodec() *codec.Codec {
cdc := codec.New()
cdc.RegisterInterface((*sdk.Msg)(nil), nil)
cdc.RegisterConcrete(MsgSend{}, "example/MsgSend", nil)
cdc.RegisterConcrete(MsgIssue{}, "example/MsgIssue", nil)
auth.RegisterCodec(cdc)
cryptoAmino.RegisterAmino(cdc)
return cdc
}

View File

@ -1,100 +0,0 @@
package app
import (
abci "github.com/tendermint/tendermint/abci/types"
cmn "github.com/tendermint/tendermint/libs/common"
dbm "github.com/tendermint/tendermint/libs/db"
"github.com/tendermint/tendermint/libs/log"
bapp "github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"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 := UpdatedCodec()
// Create the base application object.
app := bapp.NewBaseApp(app4Name, logger, db, auth.DefaultTxDecoder(cdc))
// Create a key for accessing the account store.
keyAccount := sdk.NewKVStoreKey(auth.StoreKey)
// Set various mappers/keepers to interact easily with underlying stores
accountKeeper := auth.NewAccountKeeper(cdc, keyAccount, auth.ProtoBaseAccount)
bankKeeper := bank.NewBaseKeeper(accountKeeper)
// TODO
keyFees := sdk.NewKVStoreKey(auth.FeeStoreKey)
feeKeeper := auth.NewFeeCollectionKeeper(cdc, keyFees)
app.SetAnteHandler(auth.NewAnteHandler(accountKeeper, feeKeeper))
// Set InitChainer
app.SetInitChainer(NewInitChainer(cdc, accountKeeper))
// Register message routes.
// Note the handler gets access to the account store.
app.Router().
AddRoute("bank", bank.NewHandler(bankKeeper))
// 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.AccAddress `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 *codec.Codec, accountKeeper auth.AccountKeeper) 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 = accountKeeper.GetNextAccountNumber(ctx)
accountKeeper.SetAccount(ctx, acc)
}
return abci.ResponseInitChain{}
}
}

View File

@ -1,142 +0,0 @@
package app
import (
"encoding/json"
"os"
"testing"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto/ed25519"
dbm "github.com/tendermint/tendermint/libs/db"
"github.com/tendermint/tendermint/libs/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"
)
// Create and return App4 instance
func newTestChain() *bapp.BaseApp {
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "sdk/app")
db := dbm.NewMemDB()
return NewApp4(logger, db)
}
// Initialize all provided addresses with 100 testCoin
func InitTestChain(bc *bapp.BaseApp, chainID string, addrs ...sdk.AccAddress) {
var accounts []*GenesisAccount
for _, addr := range addrs {
acc := GenesisAccount{
Address: addr,
Coins: sdk.Coins{sdk.NewCoin("testCoin", sdk.NewInt(100))},
}
accounts = append(accounts, &acc)
}
accountState := GenesisState{accounts}
genState, err := json.Marshal(accountState)
if err != nil {
panic(err)
}
bc.InitChain(abci.RequestInitChain{ChainId: chainID, AppStateBytes: genState})
}
// Generate basic SpendMsg with one input and output
func GenerateSpendMsg(sender, receiver sdk.AccAddress, amount sdk.Coins) bank.MsgSend {
return bank.MsgSend{
Inputs: []bank.Input{{sender, amount}},
Outputs: []bank.Output{{receiver, amount}},
}
}
// Test spending nonexistent funds fails
func TestBadMsg(t *testing.T) {
bc := newTestChain()
// Create privkeys and addresses
priv1 := ed25519.GenPrivKey()
priv2 := ed25519.GenPrivKey()
addr1 := priv1.PubKey().Address().Bytes()
addr2 := priv2.PubKey().Address().Bytes()
// Attempt to spend non-existent funds
msg := GenerateSpendMsg(addr1, addr2, sdk.Coins{sdk.NewCoin("testCoin", sdk.NewInt(100))})
// Construct transaction
fee := auth.StdFee{
Gas: 1000000000000000,
Amount: sdk.Coins{sdk.NewCoin("testCoin", sdk.NewInt(0))},
}
signBytes := auth.StdSignBytes("test-chain", 0, 0, fee, []sdk.Msg{msg}, "")
sig, err := priv1.Sign(signBytes)
if err != nil {
panic(err)
}
sigs := []auth.StdSignature{{
PubKey: priv1.PubKey(),
Signature: sig,
AccountNumber: 0,
Sequence: 0,
}}
tx := auth.StdTx{
Msgs: []sdk.Msg{msg},
Fee: fee,
Signatures: sigs,
Memo: "",
}
bc.BeginBlock(abci.RequestBeginBlock{Header: abci.Header{ChainID: "test-chain"}})
// Deliver the transaction
res := bc.Deliver(tx)
// Check that tx failed
require.False(t, res.IsOK(), "Invalid tx passed")
}
func TestMsgSend(t *testing.T) {
bc := newTestChain()
priv1 := ed25519.GenPrivKey()
priv2 := ed25519.GenPrivKey()
addr1 := priv1.PubKey().Address().Bytes()
addr2 := priv2.PubKey().Address().Bytes()
InitTestChain(bc, "test-chain", addr1)
// Send funds to addr2
msg := GenerateSpendMsg(addr1, addr2, sdk.Coins{sdk.NewCoin("testCoin", sdk.NewInt(100))})
fee := auth.StdFee{
Gas: 1000000000000000,
Amount: sdk.Coins{sdk.NewCoin("testCoin", sdk.NewInt(0))},
}
signBytes := auth.StdSignBytes("test-chain", 0, 0, fee, []sdk.Msg{msg}, "")
sig, err := priv1.Sign(signBytes)
if err != nil {
panic(err)
}
sigs := []auth.StdSignature{{
PubKey: priv1.PubKey(),
Signature: sig,
AccountNumber: 0,
Sequence: 0,
}}
tx := auth.StdTx{
Msgs: []sdk.Msg{msg},
Fee: fee,
Signatures: sigs,
Memo: "",
}
bc.BeginBlock(abci.RequestBeginBlock{})
res := bc.Deliver(tx)
require.True(t, res.IsOK(), res.Log)
}

View File

@ -1,16 +0,0 @@
# 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

@ -1,72 +0,0 @@
# 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,
ensuring that only parts of the program with access to the key can access the store.
The goals of the MultiStore are as follows:
- Enforce separation of concerns at the storage level
- Restrict access to storage using capabilities
- Support multiple Store implementations in a single MultiStore, for instance the Tendermint IAVL tree and
the Ethereum Patricia Trie
- 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,
which defines a simple key-value store. In the future,
we may support more kinds of stores, such as a HeapStore
or a NDStore for multidimensional storage.
## Mounting Stores
Stores are mounted during application setup. To mount some stores, first create
their capability-keys:
```
fooKey := sdk.NewKVStoreKey("foo")
barKey := sdk.NewKVStoreKey("bar")
catKey := sdk.NewKVStoreKey("cat")
```
Stores are mounted directly on the BaseApp.
They can either specify their own database, or share the primary one already
passed to the BaseApp.
In this example, `foo` and `bar` will share the primary database, while `cat` will
specify its own:
```
catDB := dbm.NewMemDB()
app.MountStore(fooKey, sdk.StoreTypeIAVL)
app.MountStore(barKey, sdk.StoreTypeIAVL)
app.MountStoreWithDB(catKey, sdk.StoreTypeIAVL, catDB)
```
## Accessing Stores
In the Cosmos-SDK, the only way to access a store is with a capability-key.
Only modules given explicit access to the capability-key will
be able to access the corresponding store. Access to the MultiStore is mediated
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,7 +0,0 @@
**SDK by Examples** offers an alternative and complementary way to learn about the Cosmos-SDK. It contains several examples that showcase how to build a decentralised application on top of the Cosmos-SDK from start to finish.
Without further ado, let us get into it!
- [Simple governance example](./simple-governance/intro.md)
If you have an example you would like to add to the list, feel free to open a PR [here](https://github.com/cosmos/cosmos-sdk/pulls).

View File

@ -1,11 +0,0 @@
# Application Initialization
In the root of your fork of the SDK, create an `app` and `cmd` folder. In this folder, we will create the main file for our application, `app.go` and the repository to handle REST and CLI commands for our app.
```bash
mkdir app cmd
mkdir -p cmd/simplegovcli cmd/simplegovd
touch app/app.go cmd/simplegovcli/main.go cmd/simplegovd/main.go
```
We will take care of these files later in the tutorial. The first step is to take care of our simple governance module.

View File

@ -1,251 +0,0 @@
# From Module To Application
## Application structure
Now, that we have built all the pieces we need, it is time to integrate them into the application. Let us exit the `/x` director go back at the root of the SDK directory.
```bash
// At root level of directory
cd app
```
We are ready to create our simple governance application!
*Note: You can check the full file (with comments!) [here](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/app/app.go)*
The `app.go` file is the main file that defines your application. In it, you will declare all the modules you need, their keepers, handlers, stores, etc. Let us take a look at each section of this file to see how the application is constructed.
Secondly, we need to define the name of our application.
```go
const (
appName = "SimpleGovApp"
)
```
Then, let us define the structure of our application.
```go
// Extended ABCI application
type SimpleGovApp struct {
*bam.BaseApp
cdc *codec.Codec
// keys to access the substores
capKeyMainStore *sdk.KVStoreKey
capKeyAccountStore *sdk.KVStoreKey
capKeyStakingStore *sdk.KVStoreKey
capKeySimpleGovStore *sdk.KVStoreKey
// keepers
feeCollectionKeeper auth.FeeCollectionKeeper
bankKeeper bank.Keeper
stakeKeeper simplestake.Keeper
simpleGovKeeper simpleGov.Keeper
// Manage getting and setting accounts
accountKeeper auth.AccountKeeper
}
```
- Each application builds on top of the `BaseApp` template, hence the pointer.
- `cdc` is the codec used in our application.
- Then come the keys to the stores we need in our application. For our simple governance app, we need 3 stores + the main store.
- Then come the keepers and mappers.
Let us do a quick reminder so that it is clear why we need these stores and keepers. Our application is primarily based on the `simple_governance` module. However, we have established in section [Keepers for our app](module-keeper.md) that our module needs access to two other modules: the `bank` module and the `stake` module. We also need the `auth` module for basic account functionalities. Finally, we need access to the main multistore to declare the stores of each of the module we use.
## CLI and Rest server
We will need to add the newly created commands to our application. To do so, go to the `cmd` folder inside your root directory:
```bash
// At root level of directory
cd cmd
```
`simplegovd` is the folder that stores the command for running the server daemon, whereas `simplegovcli` defines the commands of your application.
### Application CLI
**File: [`cmd/simplegovcli/maing.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/cmd/simplegovcli/main.go)**
To interact with our application, let us add the commands from the `simple_governance` module to our `simpleGov` application, as well as the pre-built SDK commands:
```go
// cmd/simplegovcli/main.go
...
rootCmd.AddCommand(
client.GetCommands(
simplegovcmd.GetCmdQueryProposal("proposals", cdc),
simplegovcmd.GetCmdQueryProposals("proposals", cdc),
simplegovcmd.GetCmdQueryProposalVotes("proposals", cdc),
simplegovcmd.GetCmdQueryProposalVote("proposals", cdc),
)...)
rootCmd.AddCommand(
client.PostCommands(
simplegovcmd.PostCmdPropose(cdc),
simplegovcmd.PostCmdVote(cdc),
)...)
...
```
### Rest server
**File: [`cmd/simplegovd/main.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/cmd/simplegovd/main.go)**
The `simplegovd` command will run the daemon server as a background process. First, let us create some `utils` functions:
```go
// cmd/simplegovd/main.go
// SimpleGovAppGenState sets up the app_state and appends the simpleGov app state
func SimpleGovAppGenState(cdc *codec.Codec, appGenTxs []json.RawMessage) (appState json.RawMessage, err error) {
appState, err = server.SimpleAppGenState(cdc, appGenTxs)
if err != nil {
return
}
return
}
func newApp(logger log.Logger, db dbm.DB) abci.Application {
return app.NewSimpleGovApp(logger, db)
}
func exportAppState(logger log.Logger, db dbm.DB) (json.RawMessage, error) {
dapp := app.NewSimpleGovApp(logger, db)
return dapp.ExportAppStateJSON()
}
```
Now, let us define the command for the daemon server within the `main()` function:
```go
// cmd/simplegovd/main.go
func main() {
cdc := app.MakeCodec()
ctx := server.NewDefaultContext()
rootCmd := &cobra.Command{
Use: "simplegovd",
Short: "Simple Governance Daemon (server)",
PersistentPreRunE: server.PersistentPreRunEFn(ctx),
}
server.AddCommands(ctx, cdc, rootCmd,
server.ConstructAppCreator(newApp, "simplegov"),
server.ConstructAppExporter(exportAppState, "simplegov"))
// prepare and add flags
rootDir := os.ExpandEnv("$HOME/.simplegovd")
executor := cli.PrepareBaseCmd(rootCmd, "BC", rootDir)
executor.Execute()
}
```
## Makefile
The [Makefile](https://en.wikipedia.org/wiki/Makefile) compiles the Go program by defining a set of rules with targets and recipes. We'll need to add our application commands to it:
```
// Makefile
build_examples:
ifeq ($(OS),Windows_NT)
...
go build $(BUILD_FLAGS) -o build/simplegovd.exe ./examples/simpleGov/cmd/simplegovd
go build $(BUILD_FLAGS) -o build/simplegovcli.exe ./examples/simpleGov/cmd/simplegovcli
else
...
go build $(BUILD_FLAGS) -o build/simplegovd ./examples/simpleGov/cmd/simplegovd
go build $(BUILD_FLAGS) -o build/simplegovcli ./examples/simpleGov/cmd/simplegovcli
endif
...
install_examples:
...
go install $(BUILD_FLAGS) ./examples/simpleGov/cmd/simplegovd
go install $(BUILD_FLAGS) ./examples/simpleGov/cmd/simplegovcli
```
## Application constructor
**File: [`app/app.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/app/app.go)**
Now, we need to define the constructor for our application.
```go
func NewSimpleGovApp(logger log.Logger, db dbm.DB) *SimpleGovApp
```
In this function, we will:
- Create the codec
```go
var cdc = MakeCodec()
```
- Instantiate our application. This includes creating the keys to access each of the substores.
```go
// Create your application object.
var app = &SimpleGovApp{
BaseApp: bam.NewBaseApp(appName, cdc, logger, db),
cdc: cdc,
capKeyMainStore: sdk.NewKVStoreKey(bam.MainStoreKey),
capKeyAccountStore: sdk.NewKVStoreKey(auth.StoreKey),
capKeyStakingStore: sdk.NewKVStoreKey(stake.StoreKey),
capKeySimpleGovStore: sdk.NewKVStoreKey("simpleGov"),
}
```
- Instantiate the keepers. Note that keepers generally need access to other module's keepers. In this case, make sure you only pass an instance of the keeper for the functionality that is needed. If a keeper only needs to read in another module's store, a read-only keeper should be passed to it.
```go
app.bankKeeper = bank.NewBaseKeeper(app.accountKeeper)
app.stakeKeeper = simplestake.NewKeeper(app.capKeyStakingStore, app.bankKeeper,app.RegisterCodespace(simplestake.DefaultCodespace))
app.simpleGovKeeper = simpleGov.NewKeeper(app.capKeySimpleGovStore, app.bankKeeper, app.stakeKeeper, app.RegisterCodespace(simpleGov.DefaultCodespace))
```
- Declare the handlers.
```go
app.Router().
AddRoute("bank", bank.NewHandler(app.bankKeeper)).
AddRoute("simplestake", simplestake.NewHandler(app.stakeKeeper)).
AddRoute("simpleGov", simpleGov.NewHandler(app.simpleGovKeeper))
```
- Initialize the application.
```go
// Initialize BaseApp.
app.MountStoresIAVL(app.capKeyMainStore, app.capKeyAccountStore, app.capKeySimpleGovStore, app.capKeyStakingStore)
app.SetAnteHandler(auth.NewAnteHandler(app.accountKeeper, app.feeCollectionKeeper))
err := app.LoadLatestVersion(app.capKeyMainStore)
if err != nil {
cmn.Exit(err.Error())
}
return app
```
## Application codec
**File: [`app/app.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/app/app.go)**
Finally, we need to define the `MakeCodec()` function and register the concrete types and interface from the various modules.
```go
func MakeCodec() *codec.Codec {
var cdc = codec.New()
codec.RegisterCrypto(cdc) // Register crypto.
sdk.RegisterCodec(cdc) // Register Msgs
bank.RegisterCodec(cdc)
simplestake.RegisterCodec(cdc)
simpleGov.RegisterCodec(cdc)
// Register AppAccount
cdc.RegisterInterface((*auth.Account)(nil), nil)
cdc.RegisterConcrete(&types.AppAccount{}, "simpleGov/Account", nil)
return cdc
}
```

View File

@ -1,56 +0,0 @@
# SDK By Examples - Simple Governance Application
In this tutorial, you will learn the basics of coding an application with the Cosmos-SDK. Applications built on top of the SDK are called *Application-specific blockchains*. They are decentralised applications running on their own blockchains. The application we will build in this tutorial is a simple governance application.
Before getting in the bulk of the code, we will start by some introductory content on Tendermint, Cosmos and the programming philosophy of the SDK. Let us get started!
## Table of contents:
### Introduction - Prerequisite reading
- [Intro to Tendermint and Cosmos](/introduction/tendermint-cosmos.md)
- [Tendermint Core and ABCI](/introduction/tendermint.md)
- [Intro to Cosmos-SDK](/sdk/overview.md)
### [Setup and design](setup-and-design.md)
- [Starting your own project](setup-and-design.md#get-started)
- [Setup](setup-and-design.md#setup)
- [Application design](setup-and-design.md#application-design)
### Implementation of the application
**Important note: All the code for this application can be found [here](https://github.com/cosmos/cosmos-sdk/tree/fedekunze/module_tutorial/examples/simpleGov). Snippets will be provided throughout the tutorial, but please refer to the provided link for the full implementation details**
- [Application initialization](app-init.md)
- [Simple Governance module](simple-gov-module.md)
+ [Module initialization](simple-gov-module.md#module-initialization)
+ [Types](simple-gov-module.md#types)
+ [Keeper](simple-gov-module.md#keeper)
+ [Handler](simple-gov-module.md#handler)
+ [Wire](simple-gov-module.md#codec)
+ [Errors](simple-gov-module.md#errors)
+ [Command-Line Interface](simple-gov-module.md#command-line-interface)
+ [Rest API](simple-gov-module.md#rest-api)
- [Bridging it all together](bridging-it-all.md)
+ [Application structure](bridging-it-all.md#application-structure)
+ [Application CLI and Rest Server](bridging-it-all.md#cli-and-rest-server)
+ [Makefile](bridging-it-all.md#makefile)
+ [Application constructor](bridging-it-all.md#application-constructor)
+ [Application codec](bridging-it-all.md#application-codec)
- [Running the application](running-the-application.md)
+ [Installation](running-the-application.md#installation)
+ [Submit a proposal](running-the-application.md#submit-a-proposal)
+ [Cast a vote](running-the-application.md#cast-a-vote)
## Useful links
If you have any question regarding this tutorial or about development on the SDK, please reach out us through our official communication channels:
- [Cosmos-SDK Riot Channel](https://riot.im/app/#/room/#cosmos-sdk:matrix.org)
- [Telegram](https://t.me/cosmosproject)
Or open an issue on the SDK repo:
- [Cosmos-SDK repo](https://github.com/cosmos/cosmos-sdk/)

View File

@ -1,76 +0,0 @@
# Running The Application
## Installation
Once you have finallized your application, install it using `go get`. The following commands will install the pre-built modules and examples of the SDK as well as your `simpleGov` application:
```bash
go get github.com/<your_username>/cosmos-sdk
cd $GOPATH/src/github.com/<your_username>/cosmos-sdk
make install
make install_examples
```
Check that the app is correctly installed by typing:
```bash
simplegovcli -h
simplegovd -h
```
## Submit a proposal
Uuse the CLI to create a new proposal:
```bash
simplegovcli propose --title="Voting Period update" --description="Should we change the proposal voting period to 3 weeks?" --deposit=300Atoms
```
Or, via a json file:
```bash
simplegovcli propose --proposal="path/to/proposal.json"
```
Where proposal.json contains:
```json
{
"title": "Voting Period Update",
"description": "Should we change the proposal voting period to 3 weeks?",
"type": "Text",
"deposit": "300Atoms"
}
```
Get the details of your newly created proposal:
```bash
simplegovcli proposal 1
```
You can also check all the existing open proposals:
```bash
simplegovcli proposals --active=true
```
## Cast a vote
Let's cast a vote on the created proposal:
```bash
simplegovcli vote --proposal-id=1 --option="No"
```
Get the value of the option from your casted vote :
```bash
simplegovcli proposal-vote 1 <your_address>
```
You can also check all the casted votes of a proposal:
```bash
simplegovcli proposals-votes 1
```

View File

@ -1,122 +0,0 @@
# Setup And Design
## Get started
To get started, you just have to follow these simple steps:
1. Clone the [Cosmos-SDK](https://github.com/cosmos/cosmos-sdk/tree/develop)repo
2. Code the modules needed by your application that do not already exist.
3. Create your app directory. In the app main file, import the module you need and instantiate the different stores.
4. Launch your blockchain.
## Setup
### Prerequisites
- Have [go](https://golang.org/dl/) and [git](https://git-scm.com/downloads) installed
- Don't forget to set your `PATH` and `GOPATH`
### Setup work environment
Go to the [Cosmos-SDK repo](https://githum.com/cosmos/cosmos-sdk) and fork it. Then open a terminal and:
```bash
cd $GOPATH/src/github.com/your_username
git clone github.com/your_username/cosmos-sdk
cd cosmos-sdk
```
Now we'll add the origin Cosmos-SDK as upstream in case some cool feature or module gets merged:
```bash
git remote add upstream github.com/cosmos/cosmos-sdk
git fetch upstream
git rebase upstream/master
```
We will also create a branch dedicated to our module:
```bash
git checkout -b my_new_application
```
We are all set!
## Application design
### Application description
For this tutorial, we will code a **simple governance application**, accompagnied by a **simple governance module**. It will allow us to explain most of the basic notions required to build a functioning application on the Cosmos-SDK. Note that this is not the governance module used for the Cosmos Hub. A much more [advanced governance module](https://github.com/cosmos/cosmos-sdk/tree/develop/x/gov) will be used instead.
All the code for the `simple_governance` application can be found [here](https://github.com/gamarin2/cosmos-sdk/tree/module_tutorial/examples/simpleGov/x/simple_governance). You'll notice that the module and app aren't located at the root level of the repo but in the examples directory. This is just for convenience, you can code your module and application directly in the root directory.
Without further talk, let's get into it!
### Requirements
We will start by writting down your module's requirements. We are designing a simple governance module, in which we want:
- Simple text proposals, that any coin holder can submit.
- Proposals must be submitted with a deposit in Atoms. If the deposit is larger than a `MinDeposit`, the associated proposal enters the voting period. Otherwise it is rejected.
- Bonded Atom holders can vote on proposal on a 1 bonded Atom 1 vote basis
- Bonded Atom holders can choose between 3 options when casting a vote: `Yes`, `No` and `Abstain`.
- If, at the end of the voting period, there are more `Yes` votes than `No` votes, the proposal is accepted. Otherwise, it is rejected.
- Voting period is 2 weeks
When designing a module, it is good to adopt a certain methodology. Remember that a blockchain application is just a replicated state-machine. The state is the representation of the application at a given time. It is up to the application developer to define what the state represents, depending on the goal of the application. For example, the state of a simple cryptocurrency application will be a mapping of addresses to balances.
The state can be updated according to predefined rules. Given a state and a transaction, the state-machine (i.e. the application) will return a new state. In a blockchain application, transactions are bundled in blocks, but the logic is the same. Given a state and a set of transactions (a block), the application returns a new state. A SDK-module is just a subset of the application, but it is based on the same principles. As a result, module developers only have to define a subset of the state and a subset of the transaction types, which trigger state transitions.
In summary, we have to define:
- A `State`, which represents a subset of the current state of the application.
- `Transactions`, which contain messages that trigger state transitions.
### State
Here, we will define the types we need (excluding transaction types), as well as the stores in the multistore.
Our voting module is very simple, we only need a single type: `Proposal`. `Proposals` are item to be voted upon. They can be submitted by any user. A deposit has to be provided.
```go
type Proposal struct {
Title string // Title of the proposal
Description string // Description of the proposal
Submitter sdk.Address // Address of the submitter. Needed to refund deposit if proposal is accepted.
SubmitBlock int64 // Block at which proposal is submitted. Also the block at which voting period begins.
State string // State can be either "Open", "Accepted" or "Rejected"
YesVotes int64 // Total number of Yes votes
NoVotes int64 // Total number of No votes
AbstainVotes int64 // Total number of Abstain votes
}
```
In terms of store, we will just create one [KVStore](#kvstore) in the multistore to store `Proposals`. We will also store the `Vote` (`Yes`, `No` or `Abstain`) chosen by each voter on each proposal.
### Messages
As a module developer, what you have to define are not `Transactions`, but `Messages`. Both transactions and messages exist in the Cosmos-SDK, but a transaction differs from a message in that a message is contained in a transaction. Transactions wrap around messages and add standard information like signatures and fees. As a module developer, you do not have to worry about transactions, only messages.
Let us define the messages we need in order to modify the state. Based on the requirements above, we need to define two types of messages:
- `SubmitProposalMsg`: to submit proposals
- `VoteMsg`: to vote on proposals
```go
type SubmitProposalMsg struct {
Title string // Title of the proposal
Description string // Description of the proposal
Deposit sdk.Coins // Deposit paid by submitter. Must be > MinDeposit to enter voting period
Submitter sdk.Address // Address of the submitter
}
```
```go
type VoteMsg struct {
ProposalID int64 // ID of the proposal
Option string // Option chosen by voter
Voter sdk.Address // Address of the voter
}
```

View File

@ -1,316 +0,0 @@
# Simple Governance Module
## Module initialization
First, let us go into the module's folder and create a folder for our module.
```bash
cd x/
mkdir simple_governance
cd simple_governance
mkdir -p client/cli client/rest
touch client/cli/simple_governance.go client/rest/simple_governance.go errors.go handler.go handler_test.go keeper_keys.go keeper_test.go keeper.go test_common.go test_types.go types.go codec.go
```
Let us start by adding the files we will need. Your module's folder should look something like that:
```
x
└─── simple_governance
├─── client
│ ├─── cli
│ │ └─── simple_governance.go
│ └─── rest
│ └─── simple_governance.go
├─── errors.go
├─── handler.go
├─── keeper_keys.go
├─── keeper.go
├─── types.go
└─── codec.go
```
Let us go into the detail of each of these files.
## Types
**File: [`x/simple_governance/types.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/x/simple_governance/types.go)**
In this file, we define the custom types for our module. This includes the types from the [State](app-design.md#State) section and the custom message types defined in the [Messages](app-design#Messages) section.
For each new type that is not a message, it is possible to add methods that make sense in the context of the application. In our case, we will implement an `updateTally` function to easily update the tally of a given proposal as vote messages come in.
Messages are a bit different. They implement the `Message` interface defined in the SDK's `types` folder. Here are the methods you need to implement when you define a custom message type:
- `Type()`: This function returns the name of our module's route. When messages are processed by the application, they are routed using the string returned by the `Type()` method.
- `GetSignBytes()`: Returns the byte representation of the message. It is used to sign the message.
- `GetSigners()`: Returns address(es) of the signer(s).
- `ValidateBasic()`: This function is used to discard obviously invalid messages. It is called at the beginning of `runTx()` in the baseapp file. If `ValidateBasic()` does not return `nil`, the app stops running the transaction.
- `Get()`: A basic getter, returns some property of the message.
- `String()`: Returns a human-readable version of the message
For our simple governance messages, this means:
- `Type()` will return `"simpleGov"`
- For `SubmitProposalMsg`, we need to make sure that the attributes are not empty and that the deposit is both valid and positive. Note that this is only basic validation, we will therefore not check in this method that the sender has sufficient funds to pay for the deposit
- For `VoteMsg`, we check that the address and option are valid and that the proposalID is not negative.
- As for other methods, less customization is required. You can check the code to see a standard way of implementing these.
## Keeper
**File: [`x/simple_governance/keeper.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/x/simple_governance/keeper.go)**
### Short intro to keepers
`Keepers` are a module abstraction that handle reading/writing to the module store. This is a practical implementation of the **Object Capability Model** for Cosmos.
As module developers, we have to define keepers to interact with our module's store(s) not only from within our module, but also from other modules. When another module wants to access one of our module's store(s), a keeper for this store has to be passed to it at the application level. In practice, it will look like that:
```go
// in app.go
// instanciate keepers
keeperA = moduleA.newKeeper(app.moduleAStoreKey)
keeperB = moduleB.newKeeper(app.moduleBStoreKey)
// pass instance of keeperA to handler of module B
app.Router().
AddRoute("moduleA", moduleA.NewHandler(keeperA)).
AddRoute("moduleB", moduleB.NewHandler(keeperB, keeperA)) // Here module B can access one of module A's store via the keeperA instance
```
`KeeperA` grants a set of capabilities to the handler of module B. When developing a module, it is good practice to think about the sensitivity of the different capabilities that can be granted through keepers. For example, some module may need to read and write to module A's main store, while others only need to read it. If a module has multiple stores, then some keepers could grant access to all of them, while others would only grant access to specific sub-stores. It is the job of the module developer to make sure it is easy for application developers to instanciate a keeper with the right capabilities. Of course, the handler of a module will most likely get an unrestricted instance of that module's keeper.
### Store for our app
Before we delve into the keeper itself, let us see what objects we need to store in our governance sub-store, and how to index them.
- `Proposals` will be indexed by `'proposals'|<proposalID>`.
- `Votes` (`Yes`, `No`, `Abstain`) will be indexed by `'proposals'|<proposalID>|'votes'|<voterAddress>`.
Notice the quote mark on `'proposals'` and `'votes'`. They indicate that these are constant keywords. So, for example, the option casted by voter with address `0x01` on proposal `0101` will be stored at index `'proposals'|0101|'votes'|0x01`.
These keywords are used to faciliate range queries. Range queries (TODO: Link to formal spec) allow developer to query a subspace of the store, and return an iterator. They are made possible by the nice properties of the [IAVL+ tree](https://github.com/tendermint/iavl) that is used in the background. In practice, this means that it is possible to store and query a Key-Value pair in O(1), while still being able to iterate over a given subspace of Key-Value pairs. For example, we can query all the addresses that voted on a given proposal, along with their votes, by calling `rangeQuery(SimpleGovStore, <proposalID|'addresses'>)`.
### Keepers for our app
In our case, we only have one store to access, the `SimpleGov` store. We will need to set and get values inside this store via our keeper. However, these two actions do not have the same impact in terms of security. While there should no problem in granting read access to our store to other modules, write access is way more sensitive. So ideally application developers should be able to create either a governance mapper that can only get values from the store, or one that can both get and set values. To this end, we will introduce two keepers: `Keeper` and `KeeperRead`. When application developers create their application, they will be able to decide which of our module's keeper to use.
Now, let us try to think about which keeper from **external** modules our module's keepers need access to.
Each proposal requires a deposit. This means our module needs to be able to both read and write to the module that handles tokens, which is the `bank` module. We also need to be able to determine the voting power of each voter based on their stake. To this end, we need read access to the store of the `staking` module. However, we don't need write access to this store. We should therefore indicate that in our module, and the application developer should be careful to only pass a read-only keeper of the `staking` module to our module's handler.
With all that in mind, we can define the structure of our `Keeper`:
```go
type Keeper struct {
SimpleGov sdk.StoreKey // Key to our module's store
cdc *codec.Codec // Codec to encore/decode structs
ck bank.Keeper // Needed to handle deposits. This module onlyl requires read/writes to Atom balance
sm stake.Keeper // Needed to compute voting power. This module only needs read access to the staking store.
codespace sdk.CodespaceType // Reserves space for error codes
}
```
And the structure of our `KeeperRead`:
```go
type KeeperRead struct {
Keeper
}
```
`KeeperRead` will inherit all methods from `Keeper`, except those that we override. These will be the methods that perform writes to the store.
### Functions and Methods
The first function we have to create is the constructor.
```go
func NewKeeper(SimpleGov sdk.StoreKey, ck bank.Keeper, sm stake.Keeper, codespace sdk.CodespaceType) Keeper
```
This function is called from the main [`app.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/app/app.go) file to instanciate a new `Keeper`. A similar function exits for `KeeperRead`.
```go
func NewKeeperRead(SimpleGov sdk.StoreKey, ck bank.Keeper, sm stake.Keeper, codespace sdk.CodespaceType) KeeperRead
```
Depending on the needs of the application and its modules, either `Keeper`, `KeeperRead`, or both, will be instanciated at application level.
*Note: Both the `Keeper` type name and `NewKeeper()` function's name are standard names used in every module. It is no requirement to follow this standard, but doing so can facilitate the life of application developers*
Now, let us describe the methods we need for our module's `Keeper`. For the full implementation, please refer to `keeper.go`.
- `GetProposal`: Get a `Proposal` given a `proposalID`. Proposals need to be decoded from `byte` before they can be read.
- `SetProposal`: Set a `Proposal` at index `'proposals'|<proposalID>`. Proposals need to be encoded to `byte` before they can be stored.
- `NewProposalID`: A function to generate a new unique `proposalID`.
- `GetVote`: Get a vote `Option` given a `proposalID` and a `voterAddress`.
- `SetVote`: Set a vote `Option` given a `proposalID` and a `voterAddress`.
- Proposal Queue methods: These methods implement a standard proposal queue to store `Proposals` on a First-In First-Out basis. It is used to tally the votes at the end of the voting period.
The last thing that needs to be done is to override certain methods for the `KeeperRead` type. `KeeperRead` should not have write access to the stores. Therefore, we will override the methods `SetProposal()`, `SetVote()` and `NewProposalID()`, as well as `setProposalQueue()` from the Proposal Queue's methods. For `KeeperRead`, these methods will just throw an error.
*Note: If you look at the code, you'll notice that the context `ctx` is a parameter of many of the methods. The context `ctx` provides useful information on the current state such as the current block height and allows the keeper `k` to access the `KVStore`. You can check all the methods of `ctx` [here](https://github.com/cosmos/cosmos-sdk/blob/develop/types/context.go#L144-L168)*.
## Handler
**File: [`x/simple_governance/handler.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/x/simple_governance/handler.go)**
### Constructor and core handlers
Handlers implement the core logic of the state-machine. When a transaction is routed from the app to the module, it is run by the `handler` function.
In practice, one `handler` will be implemented for each message of the module. In our case, we have two message types. We will therefore need two `handler` functions. We will also need a constructor function to route the message to the correct `handler`:
```go
func NewHandler(k Keeper) sdk.Handler {
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
switch msg := msg.(type) {
case SubmitProposalMsg:
return handleSubmitProposalMsg(ctx, k, msg)
case VoteMsg:
return handleVoteMsg(ctx, k, msg)
default:
errMsg := "Unrecognized gov Msg type: " + reflect.TypeOf(msg).Name()
return sdk.ErrUnknownRequest(errMsg).Result()
}
}
}
```
The messages are routed to the appropriate `handler` depending on their type. For our simple governance module, we only have two `handlers`, that correspond to our two message types. They have similar signatures:
```go
func handleSubmitProposalMsg(ctx sdk.Context, k Keeper, msg SubmitProposalMsg) sdk.Result
```
Let us take a look at the parameters of this function:
- The context `ctx` to access the stores.
- The keeper `k` allows the handler to read and write from the different stores, including the module's store (`SimpleGovernance` in our case) and all the stores from other modules that the keeper `k` has been granted an access to (`stake` and `bank` in our case).
- The message `msg` that holds all the information provided by the sender of the transaction.
The function returns a `Result` that is returned to the application. It contains several useful information such as the amount of `Gas` for this transaction and wether the message was succesfully processed or not. At this point, we exit the boundaries of our simple governance module and go back to root application level. The `Result` will differ from application to application. You can check the `sdk.Result` type directly [here](https://github.com/cosmos/cosmos-sdk/blob/develop/types/result.go) for more info.
### BeginBlocker and EndBlocker
In contrast to most smart-contracts platform, it is possible to perform automatic (i.e. not triggered by a transaction sent by an end-user) execution of logic in Cosmos-SDK applications.
This automatic execution of code takes place in the `BeginBlock` and `EndBlock` functions that are called at the beginning and at the end of every block. They are powerful tools, but it is important for application developers to be careful with them. For example, it is crutial that developers control the amount of computing that happens in these functions, as expensive computation could delay the block time, and never-ending loop freeze the chain altogether.
`BeginBlock` and `EndBlock` are composable functions, meaning that each module can implement its own `BeginBlock` and `EndBlock` logic. When needed, `BeginBlock` and `EndBlock` logic is implemented in the module's `handler`. Here is the standard way to proceed for `EndBlock` (`BeginBlock` follows the exact same pattern):
```go
func NewEndBlocker(k Keeper) sdk.EndBlocker {
return func(ctx sdk.Context, req abci.RequestEndBlock) (res abci.ResponseEndBlock) {
err := checkProposal(ctx, k)
if err != nil {
panic(err)
}
return
}
}
```
Do not forget that each module need to declare its `BeginBlock` and `EndBlock` constructors at application level. See the [Application - Bridging it all together](app-structure.md).
For the purpose of our simple governance application, we will use `EndBlock` to automatically tally the results of the vote. Here are the different steps that will be performed:
1. Get the oldest proposal from the `ProposalProcessingQueue`
2. Check if the `CurrentBlock` is the block at which the voting period for this proposal ends. If Yes, go to 3.. If no, exit.
3. Check if proposal is accepted or rejected. Update the proposal status.
4. Pop the proposal from the `ProposalProcessingQueue` and go back to 1.
Let us perform a quick safety analysis on this process.
- The loop will not run forever because the number of proposals in `ProposalProcessingQueue` is finite
- The computation should not be too expensive because tallying of individual proposals is not expensive and the number of proposals is expected be relatively low. That is because proposals require a `Deposit` to be accepted. `MinDeposit` should be high enough so that we don't have too many `Proposals` in the queue.
- In the eventuality that the application becomes so successful that the `ProposalProcessingQueue` ends up containing so many proposals that the blockchain starts slowing down, the module should be modified to mitigate the situation. One clever way of doing it is to cap the number of iteration per individual `EndBlock` at `MaxIteration`. This way, tallying will be spread over many blocks if the number of proposals is too important and block time should remain stable. This would require to modify the current check `if (CurrentBlock == Proposal.SubmitBlock + VotingPeriod)` to `if (CurrentBlock > Proposal.SubmitBlock + VotingPeriod) AND (Proposal.Status == ProposalStatusActive)`.
## Codec
**File: [`x/simple_governance/codec.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/x/simple_governance/codec.go)**
The `codec.go` file allows developers to register the concrete message types of their module into the codec. In our case, we have two messages to declare:
```go
func RegisterCodec(cdc *codec.Codec) {
cdc.RegisterConcrete(SubmitProposalMsg{}, "simple_governance/SubmitProposalMsg", nil)
cdc.RegisterConcrete(VoteMsg{}, "simple_governance/VoteMsg", nil)
}
```
Don't forget to call this function in `app.go` (see [Application - Bridging it all together](app-structure.md)) for more).
## Errors
**File: [`x/simple_governance/errors.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/x/simple_governance/errors.go)**
The `error.go` file allows us to define custom error messages for our module. Declaring errors should be relatively similar in all modules. You can look in the `error.go` file directly for a concrete example. The code is self-explanatory.
Note that the errors of our module inherit from the `sdk.Error` interface and therefore possess the method `Result()`. This method is useful when there is an error in the `handler` and an error has to be returned in place of an actual result.
## Command-Line Interface
**File: [`x/simple_governance/client/cli/simple_governance.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/x/simple_governance/client/cli/simple_governance.go)**
Go in the `cli` folder and create a `simple_governance.go` file. This is where we will define the commands for our module.
The CLI builds on top of [Cobra](https://github.com/spf13/cobra). Here is the schema to build a command on top of Cobra:
```go
// Declare flags
const(
Flag = "flag"
...
)
// Main command function. One function for each command.
func Command(codec *codec.Codec) *cobra.Command {
// Create the command to return
command := &cobra.Command{
Use: "actual command",
Short: "Short description",
Run: func(cmd *cobra.Command, args []string) error {
// Actual function to run when command is used
},
}
// Add flags to the command
command.Flags().<Type>(FlagNameConstant, <example_value>, "<Description>")
return command
}
```
## Rest API
**File: [`x/simple_governance/client/rest/simple_governance.goo`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/x/simple_governance/client/rest/simple_governance.go)**
The Rest Server, also called [Light-Client Daemon (LCD)](https://github.com/cosmos/cosmos-sdk/tree/master/client/lcd), provides support for **HTTP queries**.
________________________________________________________
USER INTERFACE <=======> REST SERVER <=======> FULL-NODE
________________________________________________________
It allows end-users that do not want to run full-nodes themselves to interract with the chain. The LCD can be configured to perform **Light-Client verification** via the flag `--trust-node`, which can be set to `true` or `false`.
- If *light-client verification* is enabled, the Rest Server acts as a light-client and needs to be run on the end-user's machine. It allows them to interract with the chain in a trustless way without having to store the whole chain locally.
- If *light-client verification* is disabled, the Rest Server acts as a simple relayer for HTTP calls. In this setting, the Rest server needs not be run on the end-user's machine. Instead, it will probably be run by the same entity that operates the full-node the server connects to. This mode is useful if end-users trust the full-node operator and do not want to store anything locally.
Now, let us define endpoints that will be available for users to query through HTTP requests. These endpoints will be defined in a `simple_governance.go` file stored in the `rest` folder.
| Method | URL | Description |
|--------|---------------------------------|-------------------------------------------------------------|
| GET | /proposals | Range query to get all submitted proposals |
| POST | /proposals | Submit a new proposal |
| GET | /proposals/{id} | Returns a proposal given its ID |
| GET | /proposals/{id}/votes | Range query to get all the votes casted on a given proposal |
| POST | /proposals/{id}/votes | Cast a vote on a given proposal |
| GET | /proposals/{id}/votes/{address} | Returns the vote of a given address on a given proposal |
It is the job of module developers to provide sensible endpoints so that front-end developers and service providers can properly interact with it.
Additionaly, here is a [link](https://hackernoon.com/restful-api-designing-guidelines-the-best-practices-60e1d954e7c9) for REST APIs best practices.