Skip to content

Commit

Permalink
Merge branch 'master' into frojdi/rosetta-balance-tracking
Browse files Browse the repository at this point in the history
  • Loading branch information
Alessio Treglia committed Mar 11, 2021
2 parents 6354cfa + 280ee4f commit 3e47bb4
Show file tree
Hide file tree
Showing 11 changed files with 86 additions and 114 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/release-sims.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
cleanup-runs:
runs-on: ubuntu-latest
steps:
- uses: tendermint/workflow-run-cleanup-action@master
- uses: rokroskar/workflow-run-cleanup-action@master
env:
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
if: "!startsWith(github.ref, 'refs/tags/') && github.ref != 'refs/heads/master'"
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/sims.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
if: "!startsWith(github.ref, 'refs/tags/') && github.ref != 'refs/heads/master'"
steps:
- uses: tendermint/workflow-run-cleanup-action@master
- uses: rokroskar/workflow-run-cleanup-action@master
env:
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
cleanup-runs:
runs-on: ubuntu-latest
steps:
- uses: tendermint/workflow-run-cleanup-action@master
- uses: rokroskar/workflow-run-cleanup-action@master
env:
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
if: "!startsWith(github.ref, 'refs/tags/') && github.ref != 'refs/heads/master'"
Expand Down
6 changes: 3 additions & 3 deletions docs/building-modules/simulator.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ for the key-value pairs from the stores to be decoded (_i.e_ unmarshalled)
to their corresponding types. In particular, it matches the key to a concrete type
and then unmarshals the value from the `KVPair` to the type provided.

You can use the example [here](https://github.com/cosmos/cosmos-sdk/blob/master/x/distribution/simulation/decoder.go) from the distribution module to implement your store decoders.
You can use the example [here](https://github.com/cosmos/cosmos-sdk/blob/v0.42.0/x/distribution/simulation/decoder.go) from the distribution module to implement your store decoders.

### Randomized genesis

Expand All @@ -50,7 +50,7 @@ You can check an example on how to create the randomized genesis [here](https://

The simulator is able to test parameter changes at random. The simulator package from each module must contain a `RandomizedParams` func that will simulate parameter changes of the module throughout the simulations lifespan.

You can see how an example of what is needed to fully test parameter changes [here](https://github.com/cosmos/cosmos-sdk/blob/master/x/staking/simulation/params.go)
You can see how an example of what is needed to fully test parameter changes [here](https://github.com/cosmos/cosmos-sdk/blob/v0.42.0/x/staking/simulation/params.go)

### Random weighted operations

Expand Down Expand Up @@ -83,7 +83,7 @@ them to be used on the parameters.

Now that all the required functions are defined, we need to integrate them into the module pattern within the `module.go`:

+++ https://github.com/cosmos/cosmos-sdk/blob/master/x/distribution/module.go
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.42.0/x/distribution/module.go

## App Simulator manager

Expand Down
10 changes: 5 additions & 5 deletions docs/intro/sdk-app-architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,11 @@ Blockchain node | | Consensus | |

[Tendermint](https://docs.tendermint.com/v0.34/introduction/what-is-tendermint.html) is an application-agnostic engine that is responsible for handling the *networking* and *consensus* layers of a blockchain. In practice, this means that Tendermint is responsible for propagating and ordering transaction bytes. Tendermint Core relies on an eponymous Byzantine-Fault-Tolerant (BFT) algorithm to reach consensus on the order of transactions.

The Tendermint [consensus algorithm](https://docs.tendermint.com/master/introduction/what-is-tendermint.html#consensus-overview) works with a set of special nodes called *Validators*. Validators are responsible for adding blocks of transactions to the blockchain. At any given block, there is a validator set V. A validator in V is chosen by the algorithm to be the proposer of the next block. This block is considered valid if more than two thirds of V signed a *[prevote](https://docs.tendermint.com/master/spec/consensus/consensus.html#prevote-step-height-h-round-r)* and a *[precommit](https://docs.tendermint.com/master/spec/consensus/consensus.html#precommit-step-height-h-round-r)* on it, and if all the transactions that it contains are valid. The validator set can be changed by rules written in the state-machine.
The Tendermint [consensus algorithm](https://docs.tendermint.com/v0.34/introduction/what-is-tendermint.html#consensus-overview) works with a set of special nodes called *Validators*. Validators are responsible for adding blocks of transactions to the blockchain. At any given block, there is a validator set V. A validator in V is chosen by the algorithm to be the proposer of the next block. This block is considered valid if more than two thirds of V signed a *[prevote](https://docs.tendermint.com/v0.34/spec/consensus/consensus.html#prevote-step-height-h-round-r)* and a *[precommit](https://docs.tendermint.com/v0.34/spec/consensus/consensus.html#precommit-step-height-h-round-r)* on it, and if all the transactions that it contains are valid. The validator set can be changed by rules written in the state-machine.

## ABCI

Tendermint passes transactions to the application through an interface called the [ABCI](https://docs.tendermint.com/master/spec/abci/), which the application must implement.
Tendermint passes transactions to the application through an interface called the [ABCI](https://docs.tendermint.com/v0.34/spec/abci/), which the application must implement.

```
+---------------------+
Expand All @@ -86,11 +86,11 @@ Note that **Tendermint only handles transaction bytes**. It has no knowledge of

Here are the most important messages of the ABCI:

- `CheckTx`: When a transaction is received by Tendermint Core, it is passed to the application to check if a few basic requirements are met. `CheckTx` is used to protect the mempool of full-nodes against spam transactions. A special handler called the [`AnteHandler`](../basics/gas-fees.md#antehandler) is used to execute a series of validation steps such as checking for sufficient fees and validating the signatures. If the checks are valid, the transaction is added to the [mempool](https://docs.tendermint.com/master/tendermint-core/mempool.html#mempool) and relayed to peer nodes. Note that transactions are not processed (i.e. no modification of the state occurs) with `CheckTx` since they have not been included in a block yet.
- `DeliverTx`: When a [valid block](https://docs.tendermint.com/master/spec/blockchain/blockchain.html#validation) is received by Tendermint Core, each transaction in the block is passed to the application via `DeliverTx` in order to be processed. It is during this stage that the state transitions occur. The `AnteHandler` executes again along with the actual [`Msg` service methods](../building-modules/msg-services.md) for each message in the transaction.
- `CheckTx`: When a transaction is received by Tendermint Core, it is passed to the application to check if a few basic requirements are met. `CheckTx` is used to protect the mempool of full-nodes against spam transactions. A special handler called the [`AnteHandler`](../basics/gas-fees.md#antehandler) is used to execute a series of validation steps such as checking for sufficient fees and validating the signatures. If the checks are valid, the transaction is added to the [mempool](https://docs.tendermint.com/v0.34/tendermint-core/mempool.html#mempool) and relayed to peer nodes. Note that transactions are not processed (i.e. no modification of the state occurs) with `CheckTx` since they have not been included in a block yet.
- `DeliverTx`: When a [valid block](https://docs.tendermint.com/v0.34/spec/blockchain/blockchain.html#validation) is received by Tendermint Core, each transaction in the block is passed to the application via `DeliverTx` in order to be processed. It is during this stage that the state transitions occur. The `AnteHandler` executes again along with the actual [`Msg` service methods](../building-modules/msg-services.md) for each message in the transaction.
- `BeginBlock`/`EndBlock`: These messages are executed at the beginning and the end of each block, whether the block contains transaction or not. It is useful to trigger automatic execution of logic. Proceed with caution though, as computationally expensive loops could slow down your blockchain, or even freeze it if the loop is infinite.

Find a more detailed view of the ABCI methods from the [Tendermint docs](https://docs.tendermint.com/master/spec/abci/abci.html#overview).
Find a more detailed view of the ABCI methods from the [Tendermint docs](https://docs.tendermint.com/v0.34/spec/abci/abci.html#overview).

Any application built on Tendermint needs to implement the ABCI interface in order to communicate with the underlying local Tendermint engine. Fortunately, you do not have to implement the ABCI interface. The Cosmos SDK provides a boilerplate implementation of it in the form of [baseapp](./sdk-design.md#baseapp).

Expand Down
3 changes: 2 additions & 1 deletion docs/intro/why-app-specific.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ Application-Specific Blockchains are designed to address these shortcomings.

Application-specific blockchains give maximum flexibility to developers:

- In Cosmos blockchains, the state-machine is typically connected to the underlying consensus engine via an interface called the [ABCI](https://docs.tendermint.com/master/spec/abci/). This interface can be wrapped in any programming language, meaning developers can build their state-machine in the programming language of their choice.
- In Cosmos blockchains, the state-machine is typically connected to the underlying consensus engine via an interface called the [ABCI](https://docs.tendermint.com/v0.34/spec/abci/). This interface can be wrapped in any programming language, meaning developers can build their state-machine in the programming language of their choice.

- Developers can choose among multiple frameworks to build their state-machine. The most widely used today is the Cosmos SDK, but others exist (e.g. [Lotion](https://github.com/nomic-io/lotion), [Weave](https://github.com/iov-one/weave), ...). The choice will most of the time be done based on the programming language they want to use (Cosmos SDK and Weave are in Golang, Lotion is in Javascript, ...).
- The ABCI also allows developers to swap the consensus engine of their application-specific blockchain. Today, only Tendermint is production-ready, but in the future other consensus engines are expected to emerge.
- Even when they settle for a framework and consensus engine, developers still have the freedom to tweak them if they don't perfectly match their requirements in their pristine forms.
Expand Down
18 changes: 1 addition & 17 deletions simapp/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,11 +130,6 @@ var (
stakingtypes.NotBondedPoolName: {authtypes.Burner, authtypes.Staking},
govtypes.ModuleName: {authtypes.Burner},
}

// module accounts that are allowed to receive tokens
allowedReceivingModAcc = map[string]bool{
distrtypes.ModuleName: true,
}
)

var (
Expand Down Expand Up @@ -242,7 +237,7 @@ func NewSimApp(
appCodec, keys[authtypes.StoreKey], app.GetSubspace(authtypes.ModuleName), authtypes.ProtoBaseAccount, maccPerms,
)
app.BankKeeper = bankkeeper.NewBaseKeeper(
appCodec, keys[banktypes.StoreKey], app.AccountKeeper, app.GetSubspace(banktypes.ModuleName), app.BlockedAddrs(),
appCodec, keys[banktypes.StoreKey], app.AccountKeeper, app.GetSubspace(banktypes.ModuleName), app.ModuleAccountAddrs(),
)
stakingKeeper := stakingkeeper.NewKeeper(
appCodec, keys[stakingtypes.StoreKey], app.AccountKeeper, app.BankKeeper, app.GetSubspace(stakingtypes.ModuleName),
Expand Down Expand Up @@ -444,17 +439,6 @@ func (app *SimApp) ModuleAccountAddrs() map[string]bool {
return modAccAddrs
}

// BlockedAddrs returns all the app's module account addresses that are not
// allowed to receive external tokens.
func (app *SimApp) BlockedAddrs() map[string]bool {
blockedAddrs := make(map[string]bool)
for acc := range maccPerms {
blockedAddrs[authtypes.NewModuleAddress(acc).String()] = !allowedReceivingModAcc[acc]
}

return blockedAddrs
}

// LegacyAmino returns SimApp's amino codec.
//
// NOTE: This is solely to be used for testing purposes as it may be desirable
Expand Down
7 changes: 5 additions & 2 deletions simapp/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,11 @@ func TestSimAppExportAndBlockedAddrs(t *testing.T) {
app := NewSimApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, nil, true, map[int64]bool{}, DefaultNodeHome, 0, encCfg, EmptyAppOptions{})

for acc := range maccPerms {
require.Equal(t, !allowedReceivingModAcc[acc], app.BankKeeper.BlockedAddr(app.AccountKeeper.GetModuleAddress(acc)),
"ensure that blocked addresses are properly set in bank keeper")
require.True(
t,
app.BankKeeper.BlockedAddr(app.AccountKeeper.GetModuleAddress(acc)),
"ensure that blocked addresses are properly set in bank keeper",
)
}

genesisState := NewDefaultGenesisState(encCfg.Marshaler)
Expand Down
2 changes: 1 addition & 1 deletion x/auth/spec/03_antehandlers.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
order: 3
-->

# AnthHandlers
# AnteHandlers

## Handlers

Expand Down
80 changes: 0 additions & 80 deletions x/bank/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
"github.com/cosmos/cosmos-sdk/x/bank/types"
distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types"
)

type (
Expand Down Expand Up @@ -121,85 +120,6 @@ func TestSendNotEnoughBalance(t *testing.T) {
require.Equal(t, res2.GetSequence(), origSeq+1)
}

// A module account cannot be the recipient of bank sends unless it has been marked as such
func TestSendToModuleAcc(t *testing.T) {
tests := []struct {
name string
fromBalance sdk.Coins
msg *types.MsgSend
expSimPass bool
expPass bool
expFromBalance sdk.Coins
expToBalance sdk.Coins
}{
{
name: "Normal module account cannot be the recipient of bank sends",
fromBalance: coins,
msg: types.NewMsgSend(addr1, moduleAccAddr, coins),
expSimPass: false,
expPass: false,
expFromBalance: coins,
expToBalance: sdk.NewCoins(),
},
{
name: "Allowed module account can be the recipient of bank sends",
fromBalance: coins,
msg: types.NewMsgSend(addr1, authtypes.NewModuleAddress(distrtypes.ModuleName), coins),
expPass: true,
expSimPass: true,
expFromBalance: sdk.NewCoins(),
expToBalance: coins,
},
}

for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
acc := &authtypes.BaseAccount{
Address: test.msg.FromAddress,
}

genAccs := []authtypes.GenesisAccount{acc}
app := simapp.SetupWithGenesisAccounts(genAccs)
ctx := app.BaseApp.NewContext(false, tmproto.Header{})

fromAddr, err := sdk.AccAddressFromBech32(test.msg.FromAddress)
require.NoError(t, err)
toAddr, err := sdk.AccAddressFromBech32(test.msg.ToAddress)
require.NoError(t, err)

require.NoError(t, simapp.FundAccount(app, ctx, fromAddr, test.fromBalance))

app.Commit()

res1 := app.AccountKeeper.GetAccount(ctx, fromAddr)
require.NotNil(t, res1)
require.Equal(t, acc, res1.(*authtypes.BaseAccount))

origAccNum := res1.GetAccountNumber()
origSeq := res1.GetSequence()

header := tmproto.Header{Height: app.LastBlockHeight() + 1}
txGen := simapp.MakeTestEncodingConfig().TxConfig
_, _, err = simapp.SignCheckDeliver(t, txGen, app.BaseApp, header, []sdk.Msg{test.msg}, "", []uint64{origAccNum}, []uint64{origSeq}, test.expSimPass, test.expPass, priv1)
if test.expPass {
require.NoError(t, err)
} else {
require.Error(t, err)
}

simapp.CheckBalance(t, app, fromAddr, test.expFromBalance)
simapp.CheckBalance(t, app, toAddr, test.expToBalance)

res2 := app.AccountKeeper.GetAccount(app.NewContext(true, tmproto.Header{}), addr1)
require.NotNil(t, res2)

require.Equal(t, res2.GetAccountNumber(), origAccNum)
require.Equal(t, res2.GetSequence(), origSeq+1)
})
}
}

func TestMsgMultiSendWithAccounts(t *testing.T) {
acc := &authtypes.BaseAccount{
Address: addr1.String(),
Expand Down
68 changes: 66 additions & 2 deletions x/bank/handler_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package bank
package bank_test

import (
"strings"
Expand All @@ -7,13 +7,20 @@ import (
"github.com/stretchr/testify/require"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"

"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
"github.com/cosmos/cosmos-sdk/simapp"
"github.com/cosmos/cosmos-sdk/testutil/testdata"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
"github.com/cosmos/cosmos-sdk/x/bank"
bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper"
"github.com/cosmos/cosmos-sdk/x/bank/types"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
)

func TestInvalidMsg(t *testing.T) {
h := NewHandler(nil)
h := bank.NewHandler(nil)

res, err := h(sdk.NewContext(nil, tmproto.Header{}, false, nil), testdata.NewTestMsg())
require.Error(t, err)
Expand All @@ -22,3 +29,60 @@ func TestInvalidMsg(t *testing.T) {
_, _, log := sdkerrors.ABCIInfo(err, false)
require.True(t, strings.Contains(log, "unrecognized bank message type"))
}

// A module account cannot be the recipient of bank sends unless it has been marked as such
func TestSendToModuleAccount(t *testing.T) {
priv1 := secp256k1.GenPrivKey()
addr1 := sdk.AccAddress(priv1.PubKey().Address())
moduleAccAddr := authtypes.NewModuleAddress(stakingtypes.BondedPoolName)
coins := sdk.Coins{sdk.NewInt64Coin("foocoin", 10)}

tests := []struct {
name string
expectedError error
msg *types.MsgSend
}{
{
name: "not allowed module account",
msg: types.NewMsgSend(addr1, moduleAccAddr, coins),
expectedError: sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "%s is not allowed to receive funds", moduleAccAddr),
},
{
name: "allowed module account",
msg: types.NewMsgSend(addr1, authtypes.NewModuleAddress(stakingtypes.ModuleName), coins),
expectedError: nil,
},
}

acc1 := &authtypes.BaseAccount{
Address: addr1.String(),
}
accs := authtypes.GenesisAccounts{acc1}
balances := []types.Balance{
{
Address: addr1.String(),
Coins: coins,
},
}

app := simapp.SetupWithGenesisAccounts(accs, balances...)
ctx := app.BaseApp.NewContext(false, tmproto.Header{})

app.BankKeeper = bankkeeper.NewBaseKeeper(
app.AppCodec(), app.GetKey(types.StoreKey), app.AccountKeeper, app.GetSubspace(types.ModuleName), map[string]bool{
moduleAccAddr.String(): true,
},
)
handler := bank.NewHandler(app.BankKeeper)

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
_, err := handler(ctx, tc.msg)
if tc.expectedError != nil {
require.EqualError(t, err, tc.expectedError.Error())
} else {
require.NoError(t, err)
}
})
}
}

0 comments on commit 3e47bb4

Please sign in to comment.