Merge remote-tracking branch 'origin/develop' into rigel/fee-distribution
This commit is contained in:
commit
97f7dbc5e6
|
@ -154,6 +154,7 @@ jobs:
|
|||
command: |
|
||||
export PATH="$GOBIN:$PATH"
|
||||
make install
|
||||
export VERSION="$(git describe --tags --long | sed 's/v\(.*\)/\1/')"
|
||||
for pkg in $(go list github.com/cosmos/cosmos-sdk/... | grep -v github.com/cosmos/cosmos-sdk/cmd/gaia/cli_test | grep -v '/simulation' | circleci tests split --split-by=timings); do
|
||||
id=$(basename "$pkg")
|
||||
GOCACHE=off go test -timeout 8m -race -coverprofile=/tmp/workspace/profiles/$id.out -covermode=atomic "$pkg" | tee "/tmp/logs/$id-$RANDOM.log"
|
||||
|
|
10
Makefile
10
Makefile
|
@ -1,8 +1,8 @@
|
|||
PACKAGES_NOSIMULATION=$(shell go list ./... | grep -v '/simulation')
|
||||
PACKAGES_SIMTEST=$(shell go list ./... | grep '/simulation')
|
||||
COMMIT_HASH := $(shell git rev-parse --short HEAD)
|
||||
VERSION := $(shell git describe --tags --long | sed 's/v\(.*\)/\1/')
|
||||
BUILD_TAGS = netgo ledger
|
||||
BUILD_FLAGS = -tags "${BUILD_TAGS}" -ldflags "-X github.com/cosmos/cosmos-sdk/version.GitCommit=${COMMIT_HASH}"
|
||||
BUILD_FLAGS = -tags "${BUILD_TAGS}" -ldflags "-X github.com/cosmos/cosmos-sdk/version.Version=${VERSION}"
|
||||
GCC := $(shell command -v gcc 2> /dev/null)
|
||||
LEDGER_ENABLED ?= true
|
||||
UNAME_S := $(shell uname -s)
|
||||
|
@ -142,10 +142,10 @@ test_examples:
|
|||
@go test -count 1 -p 1 `go list github.com/cosmos/cosmos-sdk/examples/democoin/cli_test` -tags=cli_test
|
||||
|
||||
test_unit:
|
||||
@go test $(PACKAGES_NOSIMULATION)
|
||||
@VERSION=$(VERSION) go test $(PACKAGES_NOSIMULATION)
|
||||
|
||||
test_race:
|
||||
@go test -race $(PACKAGES_NOSIMULATION)
|
||||
@VERSION=$(VERSION) go test -race $(PACKAGES_NOSIMULATION)
|
||||
|
||||
test_sim_modules:
|
||||
@echo "Running individual module simulations..."
|
||||
|
@ -175,7 +175,7 @@ test_sim_gaia_profile:
|
|||
@go test -benchmem -run=^$$ github.com/cosmos/cosmos-sdk/cmd/gaia/app -bench ^BenchmarkFullGaiaSimulation$$ -SimulationEnabled=true -SimulationNumBlocks=$(SIM_NUM_BLOCKS) -SimulationBlockSize=$(SIM_BLOCK_SIZE) -SimulationCommit=$(SIM_COMMIT) -timeout 24h -cpuprofile cpu.out -memprofile mem.out
|
||||
|
||||
test_cover:
|
||||
@bash tests/test_cover.sh
|
||||
@export VERSION=$(VERSION); bash tests/test_cover.sh
|
||||
|
||||
test_lint:
|
||||
gometalinter.v2 --config=tools/gometalinter.json ./...
|
||||
|
|
|
@ -44,6 +44,7 @@ BREAKING CHANGES
|
|||
* [simulation] Remove log and testing.TB from Operation and Invariants, in favor of using errors \#2282
|
||||
* [tools] Removed gocyclo [#2211](https://github.com/cosmos/cosmos-sdk/issues/2211)
|
||||
* [baseapp] Remove `SetTxDecoder` in favor of requiring the decoder be set in baseapp initialization. [#1441](https://github.com/cosmos/cosmos-sdk/issues/1441)
|
||||
* [baseapp] [\#1921](https://github.com/cosmos/cosmos-sdk/issues/1921) Add minimumFees field to BaseApp.
|
||||
* [store] Change storeInfo within the root multistore to use tmhash instead of ripemd160 \#2308
|
||||
* [codec] \#2324 All referrences to wire have been renamed to codec. Additionally, wire.NewCodec is now codec.New().
|
||||
* [types] \#2343 Make sdk.Msg have a names field, to facilitate automatic tagging.
|
||||
|
@ -75,11 +76,15 @@ FEATURES
|
|||
|
||||
* Gaia
|
||||
* [cli] #2170 added ability to show the node's address via `gaiad tendermint show-address`
|
||||
* [cli] [\#1921] (https://github.com/cosmos/cosmos-sdk/issues/1921)
|
||||
* New configuration file `gaiad.toml` is now created to host Gaia-specific configuration.
|
||||
* New --minimum_fees/minimum_fees flag/config option to set a minimum fee.
|
||||
|
||||
* SDK
|
||||
* [querier] added custom querier functionality, so ABCI query requests can be handled by keepers
|
||||
* [simulation] [\#1924](https://github.com/cosmos/cosmos-sdk/issues/1924) allow operations to specify future operations
|
||||
* [simulation] [\#1924](https://github.com/cosmos/cosmos-sdk/issues/1924) Add benchmarking capabilities, with makefile commands "test_sim_gaia_benchmark, test_sim_gaia_profile"
|
||||
* [simulation] [\#2349](https://github.com/cosmos/cosmos-sdk/issues/2349) Add time-based future scheduled operations to simulator
|
||||
|
||||
* Tendermint
|
||||
|
||||
|
@ -102,6 +107,7 @@ IMPROVEMENTS
|
|||
* [x/stake] Improve speed of GetValidator, which was shown to be a performance bottleneck. [#2046](https://github.com/tendermint/tendermint/pull/2200)
|
||||
* [genesis] \#2229 Ensure that there are no duplicate accounts or validators in the genesis state.
|
||||
* Add SDK validation to `config.toml` (namely disabling `create_empty_blocks`) \#1571
|
||||
* \#1941(https://github.com/cosmos/cosmos-sdk/issues/1941) Version is now inferred via `git describe --tags`.
|
||||
|
||||
* SDK
|
||||
* [tools] Make get_vendor_deps deletes `.vendor-new` directories, in case scratch files are present.
|
||||
|
|
|
@ -68,6 +68,9 @@ type BaseApp struct {
|
|||
deliverState *state // for DeliverTx
|
||||
signedValidators []abci.SigningValidator // absent validators from begin block
|
||||
|
||||
// minimum fees for spam prevention
|
||||
minimumFees sdk.Coins
|
||||
|
||||
// flag for sealing
|
||||
sealed bool
|
||||
}
|
||||
|
@ -188,10 +191,13 @@ func (app *BaseApp) initFromStore(mainKey sdk.StoreKey) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// SetMinimumFees sets the minimum fees.
|
||||
func (app *BaseApp) SetMinimumFees(fees sdk.Coins) { app.minimumFees = fees }
|
||||
|
||||
// NewContext returns a new Context with the correct store, the given header, and nil txBytes.
|
||||
func (app *BaseApp) NewContext(isCheckTx bool, header abci.Header) sdk.Context {
|
||||
if isCheckTx {
|
||||
return sdk.NewContext(app.checkState.ms, header, true, app.Logger)
|
||||
return sdk.NewContext(app.checkState.ms, header, true, app.Logger).WithMinimumFees(app.minimumFees)
|
||||
}
|
||||
return sdk.NewContext(app.deliverState.ms, header, false, app.Logger)
|
||||
}
|
||||
|
@ -209,7 +215,7 @@ func (app *BaseApp) setCheckState(header abci.Header) {
|
|||
ms := app.cms.CacheMultiStore()
|
||||
app.checkState = &state{
|
||||
ms: ms,
|
||||
ctx: sdk.NewContext(ms, header, true, app.Logger),
|
||||
ctx: sdk.NewContext(ms, header, true, app.Logger).WithMinimumFees(app.minimumFees),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -386,7 +392,8 @@ func handleQueryCustom(app *BaseApp, path []string, req abci.RequestQuery) (res
|
|||
sdk.ErrUnknownRequest(fmt.Sprintf("no custom querier found for route %s", path[1])).QueryResult()
|
||||
}
|
||||
|
||||
ctx := sdk.NewContext(app.cms.CacheMultiStore(), app.checkState.ctx.BlockHeader(), true, app.Logger)
|
||||
ctx := sdk.NewContext(app.cms.CacheMultiStore(), app.checkState.ctx.BlockHeader(), true, app.Logger).
|
||||
WithMinimumFees(app.minimumFees)
|
||||
// Passes the rest of the path as an argument to the querier.
|
||||
// For example, in the path "custom/gov/proposal/test", the gov querier gets []string{"proposal", "test"} as the path
|
||||
resBytes, err := querier(ctx, path[2:], req)
|
||||
|
|
|
@ -20,9 +20,18 @@ func SetPruning(pruning string) func(*BaseApp) {
|
|||
case "syncable":
|
||||
pruningEnum = sdk.PruneSyncable
|
||||
default:
|
||||
panic(fmt.Sprintf("Invalid pruning strategy: %s", pruning))
|
||||
panic(fmt.Sprintf("invalid pruning strategy: %s", pruning))
|
||||
}
|
||||
return func(bap *BaseApp) {
|
||||
bap.cms.SetPruning(pruningEnum)
|
||||
}
|
||||
}
|
||||
|
||||
// SetMinimumFees returns an option that sets the minimum fees on the app.
|
||||
func SetMinimumFees(minFees string) func(*BaseApp) {
|
||||
fees, err := sdk.ParseCoins(minFees)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("invalid minimum fees: %v", err))
|
||||
}
|
||||
return func(bap *BaseApp) { bap.SetMinimumFees(fees) }
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"regexp"
|
||||
"testing"
|
||||
"time"
|
||||
|
@ -25,6 +26,7 @@ import (
|
|||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
tests "github.com/cosmos/cosmos-sdk/tests"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
version "github.com/cosmos/cosmos-sdk/version"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
authrest "github.com/cosmos/cosmos-sdk/x/auth/client/rest"
|
||||
"github.com/cosmos/cosmos-sdk/x/gov"
|
||||
|
@ -34,6 +36,7 @@ import (
|
|||
|
||||
func init() {
|
||||
cryptoKeys.BcryptSecurityParameter = 1
|
||||
version.Version = os.Getenv("VERSION")
|
||||
}
|
||||
|
||||
func TestKeys(t *testing.T) {
|
||||
|
@ -124,16 +127,16 @@ func TestVersion(t *testing.T) {
|
|||
res, body := Request(t, port, "GET", "/version", nil)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
||||
reg, err := regexp.Compile(`\d+\.\d+\.\d+(-dev)?`)
|
||||
reg, err := regexp.Compile(`\d+\.\d+\.\d+.*`)
|
||||
require.Nil(t, err)
|
||||
match := reg.MatchString(body)
|
||||
require.True(t, match, body)
|
||||
require.True(t, match, body, fmt.Sprintf("%s", body))
|
||||
|
||||
// node info
|
||||
res, body = Request(t, port, "GET", "/node_version", nil)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
||||
reg, err = regexp.Compile(`\d+\.\d+\.\d+(-dev)?`)
|
||||
reg, err = regexp.Compile(`\d+\.\d+\.\d+.*`)
|
||||
require.Nil(t, err)
|
||||
match = reg.MatchString(body)
|
||||
require.True(t, match, body)
|
||||
|
|
|
@ -36,6 +36,72 @@ func init() {
|
|||
gaiadHome, gaiacliHome = getTestingHomeDirs()
|
||||
}
|
||||
|
||||
func TestGaiaCLIMinimumFees(t *testing.T) {
|
||||
chainID, servAddr, port := initializeFixtures(t)
|
||||
flags := fmt.Sprintf("--home=%s --node=%v --chain-id=%v", gaiacliHome, servAddr, chainID)
|
||||
|
||||
// start gaiad server with minimum fees
|
||||
proc := tests.GoExecuteTWithStdout(t, fmt.Sprintf("gaiad start --home=%s --rpc.laddr=%v --minimum_fees=2feeToken", gaiadHome, servAddr))
|
||||
|
||||
defer proc.Stop(false)
|
||||
tests.WaitForTMStart(port)
|
||||
tests.WaitForNextNBlocksTM(2, port)
|
||||
|
||||
fooAddr, _ := executeGetAddrPK(t, fmt.Sprintf("gaiacli keys show foo --output=json --home=%s", gaiacliHome))
|
||||
barAddr, _ := executeGetAddrPK(t, fmt.Sprintf("gaiacli keys show bar --output=json --home=%s", gaiacliHome))
|
||||
|
||||
fooAcc := executeGetAccount(t, fmt.Sprintf("gaiacli account %s %v", fooAddr, flags))
|
||||
require.Equal(t, int64(50), fooAcc.GetCoins().AmountOf("steak").Int64())
|
||||
|
||||
success := executeWrite(t, fmt.Sprintf(
|
||||
"gaiacli send %v --amount=10steak --to=%s --from=foo", flags, barAddr), app.DefaultKeyPass)
|
||||
require.False(t, success)
|
||||
tests.WaitForNextNBlocksTM(2, port)
|
||||
|
||||
}
|
||||
|
||||
func TestGaiaCLIFeesDeduction(t *testing.T) {
|
||||
chainID, servAddr, port := initializeFixtures(t)
|
||||
flags := fmt.Sprintf("--home=%s --node=%v --chain-id=%v", gaiacliHome, servAddr, chainID)
|
||||
|
||||
// start gaiad server with minimum fees
|
||||
proc := tests.GoExecuteTWithStdout(t, fmt.Sprintf("gaiad start --home=%s --rpc.laddr=%v --minimum_fees=1fooToken", gaiadHome, servAddr))
|
||||
|
||||
defer proc.Stop(false)
|
||||
tests.WaitForTMStart(port)
|
||||
tests.WaitForNextNBlocksTM(2, port)
|
||||
|
||||
fooAddr, _ := executeGetAddrPK(t, fmt.Sprintf("gaiacli keys show foo --output=json --home=%s", gaiacliHome))
|
||||
barAddr, _ := executeGetAddrPK(t, fmt.Sprintf("gaiacli keys show bar --output=json --home=%s", gaiacliHome))
|
||||
|
||||
fooAcc := executeGetAccount(t, fmt.Sprintf("gaiacli account %s %v", fooAddr, flags))
|
||||
require.Equal(t, int64(1000), fooAcc.GetCoins().AmountOf("fooToken").Int64())
|
||||
|
||||
// test simulation
|
||||
success := executeWrite(t, fmt.Sprintf(
|
||||
"gaiacli send %v --amount=1000fooToken --to=%s --from=foo --fee=1fooToken --dry-run", flags, barAddr), app.DefaultKeyPass)
|
||||
require.True(t, success)
|
||||
tests.WaitForNextNBlocksTM(2, port)
|
||||
// ensure state didn't change
|
||||
fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %s %v", fooAddr, flags))
|
||||
require.Equal(t, int64(1000), fooAcc.GetCoins().AmountOf("fooToken").Int64())
|
||||
|
||||
// insufficient funds (coins + fees)
|
||||
success = executeWrite(t, fmt.Sprintf(
|
||||
"gaiacli send %v --amount=1000fooToken --to=%s --from=foo --fee=1fooToken", flags, barAddr), app.DefaultKeyPass)
|
||||
require.False(t, success)
|
||||
tests.WaitForNextNBlocksTM(2, port)
|
||||
// ensure state didn't change
|
||||
fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %s %v", fooAddr, flags))
|
||||
require.Equal(t, int64(1000), fooAcc.GetCoins().AmountOf("fooToken").Int64())
|
||||
|
||||
// test success (transfer = coins + fees)
|
||||
success = executeWrite(t, fmt.Sprintf(
|
||||
"gaiacli send %v --fee=300fooToken --amount=500fooToken --to=%s --from=foo", flags, barAddr), app.DefaultKeyPass)
|
||||
require.True(t, success)
|
||||
tests.WaitForNextNBlocksTM(2, port)
|
||||
}
|
||||
|
||||
func TestGaiaCLISend(t *testing.T) {
|
||||
chainID, servAddr, port := initializeFixtures(t)
|
||||
flags := fmt.Sprintf("--home=%s --node=%v --chain-id=%v", gaiacliHome, servAddr, chainID)
|
||||
|
|
|
@ -43,7 +43,10 @@ func main() {
|
|||
}
|
||||
|
||||
func newApp(logger log.Logger, db dbm.DB, traceStore io.Writer) abci.Application {
|
||||
return app.NewGaiaApp(logger, db, traceStore, baseapp.SetPruning(viper.GetString("pruning")))
|
||||
return app.NewGaiaApp(logger, db, traceStore,
|
||||
baseapp.SetPruning(viper.GetString("pruning")),
|
||||
baseapp.SetMinimumFees(viper.GetString("minimum_fees")),
|
||||
)
|
||||
}
|
||||
|
||||
func exportAppStateAndTMValidators(
|
||||
|
|
|
@ -1,16 +1,69 @@
|
|||
# Documentation Maintenance Overview
|
||||
# Docs Build Workflow
|
||||
|
||||
The documentation found in this directory is hosted at:
|
||||
The documentation for the Cosmos SDK is hosted at:
|
||||
|
||||
- https://cosmos.network/docs/
|
||||
- https://cosmos.network/docs/ and
|
||||
- https://cosmos-staging.interblock.io/docs/
|
||||
|
||||
and built using [VuePress](https://vuepress.vuejs.org/) from the Cosmos website repo:
|
||||
built from the files in this (`/docs`) directory for
|
||||
[master](https://github.com/cosmos/cosmos-sdk/tree/master/docs)
|
||||
and [develop](https://github.com/cosmos/cosmos-sdk/tree/develop/docs),
|
||||
respectively.
|
||||
|
||||
- https://github.com/cosmos/cosmos.network
|
||||
## How It Works
|
||||
|
||||
Under the hood, Jenkins listens for changes (on develop or master) in ./docs then rebuilds
|
||||
either the staging or production site depending on which branch the changes were made.
|
||||
There is a Jenkins job listening for changes in the `/docs` directory, on both
|
||||
the `master` and `develop` branches. Any updates to files in this directory
|
||||
on those branches will automatically trigger a website deployment. Under the hood,
|
||||
a private website repository has make targets consumed by a standard Jenkins task.
|
||||
|
||||
To update the Table of Contents (layout of the documentation sidebar), edit the
|
||||
`config.js` in this directory, while the `README.md` is the landing page for the
|
||||
website documentation.
|
||||
## README
|
||||
|
||||
The [README.md](./README.md) is also the landing page for the documentation
|
||||
on the website.
|
||||
|
||||
## Config.js
|
||||
|
||||
The [config.js](./config.js) generates the sidebar and Table of Contents
|
||||
on the website docs. Note the use of relative links and the omission of
|
||||
file extensions. Additional features are available to improve the look
|
||||
of the sidebar.
|
||||
|
||||
## Links
|
||||
|
||||
**NOTE:** Strongly consider the existing links - both within this directory
|
||||
and to the website docs - when moving or deleting files.
|
||||
|
||||
Relative links should be used nearly everywhere, having discovered and weighed the following:
|
||||
|
||||
### Relative
|
||||
|
||||
Where is the other file, relative to the current one?
|
||||
|
||||
- works both on GitHub and for the VuePress build
|
||||
- confusing / annoying to have things like: `../../../../myfile.md`
|
||||
- requires more updates when files are re-shuffled
|
||||
|
||||
### Absolute
|
||||
|
||||
Where is the other file, given the root of the repo?
|
||||
|
||||
- works on GitHub, doesn't work for the VuePress build
|
||||
- this is much nicer: `/docs/hereitis/myfile.md`
|
||||
- if you move that file around, the links inside it are preserved (but not to it, of course)
|
||||
|
||||
### Full
|
||||
|
||||
The full GitHub URL to a file or directory. Used occasionally when it makes sense
|
||||
to send users to the GitHub.
|
||||
|
||||
## Building Locally
|
||||
|
||||
Not currently possible but coming soon! Doing so requires
|
||||
assets held in the (private) website repo, installing
|
||||
[VuePress](https://vuepress.vuejs.org/), and modifying the `config.js`.
|
||||
|
||||
## Consistency
|
||||
|
||||
Because the build processes are identical (as is the information contained herein), this file should be kept in sync as
|
||||
much as possible with its [counterpart in the Tendermint Core repo](https://github.com/tendermint/tendermint/blob/develop/docs/DOCS_README.md).
|
||||
|
|
|
@ -7,3 +7,8 @@ Cosmos is a decentralized network of independent parallel blockchains, each powe
|
|||
The first blockchain in the Cosmos Network is the Cosmos Hub, whose native token is the Atom. Cosmos is a permission-less network, meaning that anybody can build a blockchain on it.
|
||||
|
||||
Cosmos can interoperate with multiple other applications and cryptocurrencies. By creating a new zone, you can plug any blockchain system into the Cosmos hub and pass tokens back and forth between those zones, without the need for an intermediary.
|
||||
|
||||
## Edit the Documentation
|
||||
|
||||
See [this file](./DOCS_README.md) for details of the build process and
|
||||
considerations when making changes.
|
||||
|
|
|
@ -29,6 +29,19 @@ You can edit this `name` later, in the `~/.gaiad/config/config.toml` file:
|
|||
moniker = "<your_custom_name>"
|
||||
```
|
||||
|
||||
You can edit the `~/.gaiad/config/gaiad.toml` file in order to enable the anti spam mechanism and reject incoming transactions with less than a minimum fee:
|
||||
|
||||
```
|
||||
# This is a TOML config file.
|
||||
# For more information, see https://github.com/toml-lang/toml
|
||||
|
||||
##### main base config options #####
|
||||
|
||||
# Validators reject any tx from the mempool with less than the minimum fee per gas.
|
||||
minimum_fees = ""
|
||||
```
|
||||
|
||||
|
||||
Your full node has been initialized! Please skip to [Genesis & Seeds](#genesis-seeds).
|
||||
|
||||
## Upgrading From Previous Testnet
|
||||
|
|
|
@ -1,5 +1,41 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultMinimumFees = ""
|
||||
)
|
||||
|
||||
// BaseConfig defines the server's basic configuration
|
||||
type BaseConfig struct {
|
||||
// Tx minimum fee
|
||||
MinFees string `mapstructure:"minimum_fees"`
|
||||
}
|
||||
|
||||
// Config defines the server's top level configuration
|
||||
type Config struct {
|
||||
BaseConfig `mapstructure:",squash"`
|
||||
}
|
||||
|
||||
// SetMinimumFee sets the minimum fee.
|
||||
func (c *Config) SetMinimumFees(fees sdk.Coins) { c.MinFees = fees.String() }
|
||||
|
||||
// SetMinimumFee sets the minimum fee.
|
||||
func (c *Config) MinimumFees() sdk.Coins {
|
||||
fees, err := sdk.ParseCoins(c.MinFees)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("invalid minimum fees: %v", err))
|
||||
}
|
||||
return fees
|
||||
}
|
||||
|
||||
// DefaultConfig returns server's default configuration.
|
||||
func DefaultConfig() *Config { return &Config{BaseConfig{MinFees: defaultMinimumFees}} }
|
||||
|
||||
//_____________________________________________________________________
|
||||
|
||||
// Configuration structure for command functions that share configuration.
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestDefaultConfig(t *testing.T) {
|
||||
cfg := DefaultConfig()
|
||||
require.True(t, cfg.MinimumFees().IsZero())
|
||||
}
|
||||
|
||||
func TestSetMinimumFees(t *testing.T) {
|
||||
cfg := DefaultConfig()
|
||||
cfg.SetMinimumFees(sdk.Coins{sdk.NewCoin("foo", sdk.NewInt(100))})
|
||||
require.Equal(t, "100foo", cfg.MinFees)
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"text/template"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
)
|
||||
|
||||
const defaultConfigTemplate = `# This is a TOML config file.
|
||||
# For more information, see https://github.com/toml-lang/toml
|
||||
|
||||
##### main base config options #####
|
||||
|
||||
# Validators reject any tx from the mempool with less than the minimum fee per gas.
|
||||
minimum_fees = "{{ .BaseConfig.MinFees }}"
|
||||
`
|
||||
|
||||
var configTemplate *template.Template
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
tmpl := template.New("cosmosConfigFileTemplate")
|
||||
if configTemplate, err = tmpl.Parse(defaultConfigTemplate); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// ParseConfig retrieves the default environment configuration for Cosmos.
|
||||
func ParseConfig() (*Config, error) {
|
||||
conf := DefaultConfig()
|
||||
err := viper.Unmarshal(conf)
|
||||
return conf, err
|
||||
}
|
||||
|
||||
// WriteConfigFile renders config using the template and writes it to configFilePath.
|
||||
func WriteConfigFile(configFilePath string, config *Config) {
|
||||
var buffer bytes.Buffer
|
||||
|
||||
if err := configTemplate.Execute(&buffer, config); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
cmn.MustWriteFile(configFilePath, buffer.Bytes(), 0644)
|
||||
}
|
|
@ -19,6 +19,7 @@ const (
|
|||
flagAddress = "address"
|
||||
flagTraceStore = "trace-store"
|
||||
flagPruning = "pruning"
|
||||
flagMinimumFees = "minimum_fees"
|
||||
)
|
||||
|
||||
// StartCmd runs the service passed in, either stand-alone or in-process with
|
||||
|
@ -45,6 +46,7 @@ func StartCmd(ctx *Context, appCreator AppCreator) *cobra.Command {
|
|||
cmd.Flags().String(flagAddress, "tcp://0.0.0.0:26658", "Listen address")
|
||||
cmd.Flags().String(flagTraceStore, "", "Enable KVStore tracing to an output file")
|
||||
cmd.Flags().String(flagPruning, "syncable", "Pruning strategy: syncable, nothing, everything")
|
||||
cmd.Flags().String(flagMinimumFees, "", "Minimum fees validator will accept for transactions")
|
||||
|
||||
// add support for all Tendermint-specific command line options
|
||||
tcmd.AddNodeFlags(cmd)
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
"github.com/cosmos/cosmos-sdk/server/config"
|
||||
"github.com/cosmos/cosmos-sdk/version"
|
||||
tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
|
||||
cfg "github.com/tendermint/tendermint/config"
|
||||
|
@ -97,6 +98,20 @@ func interceptLoadConfig() (conf *cfg.Config, err error) {
|
|||
if conf == nil {
|
||||
conf, err = tcmd.ParseConfig()
|
||||
}
|
||||
|
||||
cosmosConfigFilePath := filepath.Join(rootDir, "config/gaiad.toml")
|
||||
viper.SetConfigName("cosmos")
|
||||
_ = viper.MergeInConfig()
|
||||
var cosmosConf *config.Config
|
||||
if _, err := os.Stat(cosmosConfigFilePath); os.IsNotExist(err) {
|
||||
cosmosConf, _ := config.ParseConfig()
|
||||
config.WriteConfigFile(cosmosConfigFilePath, cosmosConf)
|
||||
}
|
||||
|
||||
if cosmosConf == nil {
|
||||
_, err = config.ParseConfig()
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -493,3 +493,25 @@ func (krc *keyRangeCounter) key() int {
|
|||
//--------------------------------------------------------
|
||||
|
||||
func bz(s string) []byte { return []byte(s) }
|
||||
|
||||
func BenchmarkCacheKVStoreGetNoKeyFound(b *testing.B) {
|
||||
st := newCacheKVStore()
|
||||
b.ResetTimer()
|
||||
// assumes b.N < 2**24
|
||||
for i := 0; i < b.N; i++ {
|
||||
st.Get([]byte{byte((i & 0xFF0000) >> 16), byte((i & 0xFF00) >> 8), byte(i & 0xFF)})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkCacheKVStoreGetKeyFound(b *testing.B) {
|
||||
st := newCacheKVStore()
|
||||
for i := 0; i < b.N; i++ {
|
||||
arr := []byte{byte((i & 0xFF0000) >> 16), byte((i & 0xFF00) >> 8), byte(i & 0xFF)}
|
||||
st.Set(arr, arr)
|
||||
}
|
||||
b.ResetTimer()
|
||||
// assumes b.N < 2**24
|
||||
for i := 0; i < b.N; i++ {
|
||||
st.Get([]byte{byte((i & 0xFF0000) >> 16), byte((i & 0xFF00) >> 8), byte(i & 0xFF)})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,6 +46,12 @@ func (coin Coin) IsGTE(other Coin) bool {
|
|||
return coin.SameDenomAs(other) && (!coin.Amount.LT(other.Amount))
|
||||
}
|
||||
|
||||
// IsLT returns true if they are the same type and the receiver is
|
||||
// a smaller value
|
||||
func (coin Coin) IsLT(other Coin) bool {
|
||||
return !coin.IsGTE(other)
|
||||
}
|
||||
|
||||
// IsEqual returns true if the two sets of Coins have the same value
|
||||
func (coin Coin) IsEqual(other Coin) bool {
|
||||
return coin.SameDenomAs(other) && (coin.Amount.Equal(other.Amount))
|
||||
|
@ -181,6 +187,12 @@ func (coins Coins) IsGTE(coinsB Coins) bool {
|
|||
return diff.IsNotNegative()
|
||||
}
|
||||
|
||||
// IsLT returns True iff every currency in coins, the currency is
|
||||
// present at a smaller amount in coins
|
||||
func (coins Coins) IsLT(coinsB Coins) bool {
|
||||
return !coins.IsGTE(coinsB)
|
||||
}
|
||||
|
||||
// IsZero returns true if there are no coins
|
||||
// or all coins are zero.
|
||||
func (coins Coins) IsZero() bool {
|
||||
|
|
|
@ -76,6 +76,24 @@ func TestIsGTECoin(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestIsLTCoin(t *testing.T) {
|
||||
cases := []struct {
|
||||
inputOne Coin
|
||||
inputTwo Coin
|
||||
expected bool
|
||||
}{
|
||||
{NewInt64Coin("A", 1), NewInt64Coin("A", 1), false},
|
||||
{NewInt64Coin("A", 2), NewInt64Coin("A", 1), false},
|
||||
{NewInt64Coin("A", -1), NewInt64Coin("A", 5), true},
|
||||
{NewInt64Coin("a", 0), NewInt64Coin("b", 1), true},
|
||||
}
|
||||
|
||||
for tcIndex, tc := range cases {
|
||||
res := tc.inputOne.IsLT(tc.inputTwo)
|
||||
require.Equal(t, tc.expected, res, "coin LT relation is incorrect, tc #%d", tcIndex)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsEqualCoin(t *testing.T) {
|
||||
cases := []struct {
|
||||
inputOne Coin
|
||||
|
@ -227,6 +245,8 @@ func TestCoins(t *testing.T) {
|
|||
assert.True(t, good.IsPositive(), "Expected coins to be positive: %v", good)
|
||||
assert.False(t, null.IsPositive(), "Expected coins to not be positive: %v", null)
|
||||
assert.True(t, good.IsGTE(empty), "Expected %v to be >= %v", good, empty)
|
||||
assert.False(t, good.IsLT(empty), "Expected %v to be < %v", good, empty)
|
||||
assert.True(t, empty.IsLT(good), "Expected %v to be < %v", empty, good)
|
||||
assert.False(t, neg.IsPositive(), "Expected neg coins to not be positive: %v", neg)
|
||||
assert.Zero(t, len(sum), "Expected 0 coins")
|
||||
assert.False(t, badSort1.IsValid(), "Coins are not sorted")
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// nolint
|
||||
package types
|
||||
|
||||
import (
|
||||
|
@ -41,10 +42,12 @@ func NewContext(ms MultiStore, header abci.Header, isCheckTx bool, logger log.Lo
|
|||
c = c.WithBlockHeader(header)
|
||||
c = c.WithBlockHeight(header.Height)
|
||||
c = c.WithChainID(header.ChainID)
|
||||
c = c.WithIsCheckTx(isCheckTx)
|
||||
c = c.WithTxBytes(nil)
|
||||
c = c.WithLogger(logger)
|
||||
c = c.WithSigningValidators(nil)
|
||||
c = c.WithGasMeter(NewInfiniteGasMeter())
|
||||
c = c.WithMinimumFees(Coins{})
|
||||
return c
|
||||
}
|
||||
|
||||
|
@ -132,10 +135,12 @@ const (
|
|||
contextKeyBlockHeight
|
||||
contextKeyConsensusParams
|
||||
contextKeyChainID
|
||||
contextKeyIsCheckTx
|
||||
contextKeyTxBytes
|
||||
contextKeyLogger
|
||||
contextKeySigningValidators
|
||||
contextKeyGasMeter
|
||||
contextKeyMinimumFees
|
||||
)
|
||||
|
||||
// NOTE: Do not expose MultiStore.
|
||||
|
@ -145,41 +150,41 @@ func (c Context) multiStore() MultiStore {
|
|||
return c.Value(contextKeyMultiStore).(MultiStore)
|
||||
}
|
||||
|
||||
// nolint
|
||||
func (c Context) BlockHeader() abci.Header {
|
||||
return c.Value(contextKeyBlockHeader).(abci.Header)
|
||||
}
|
||||
func (c Context) BlockHeight() int64 {
|
||||
return c.Value(contextKeyBlockHeight).(int64)
|
||||
}
|
||||
func (c Context) BlockHeader() abci.Header { return c.Value(contextKeyBlockHeader).(abci.Header) }
|
||||
|
||||
func (c Context) BlockHeight() int64 { return c.Value(contextKeyBlockHeight).(int64) }
|
||||
|
||||
func (c Context) ConsensusParams() abci.ConsensusParams {
|
||||
return c.Value(contextKeyConsensusParams).(abci.ConsensusParams)
|
||||
}
|
||||
func (c Context) ChainID() string {
|
||||
return c.Value(contextKeyChainID).(string)
|
||||
}
|
||||
func (c Context) TxBytes() []byte {
|
||||
return c.Value(contextKeyTxBytes).([]byte)
|
||||
}
|
||||
func (c Context) Logger() log.Logger {
|
||||
return c.Value(contextKeyLogger).(log.Logger)
|
||||
}
|
||||
|
||||
func (c Context) ChainID() string { return c.Value(contextKeyChainID).(string) }
|
||||
|
||||
func (c Context) TxBytes() []byte { return c.Value(contextKeyTxBytes).([]byte) }
|
||||
|
||||
func (c Context) Logger() log.Logger { return c.Value(contextKeyLogger).(log.Logger) }
|
||||
|
||||
func (c Context) SigningValidators() []abci.SigningValidator {
|
||||
return c.Value(contextKeySigningValidators).([]abci.SigningValidator)
|
||||
}
|
||||
func (c Context) GasMeter() GasMeter {
|
||||
return c.Value(contextKeyGasMeter).(GasMeter)
|
||||
}
|
||||
func (c Context) WithMultiStore(ms MultiStore) Context {
|
||||
return c.withValue(contextKeyMultiStore, ms)
|
||||
}
|
||||
|
||||
func (c Context) GasMeter() GasMeter { return c.Value(contextKeyGasMeter).(GasMeter) }
|
||||
|
||||
func (c Context) IsCheckTx() bool { return c.Value(contextKeyIsCheckTx).(bool) }
|
||||
|
||||
func (c Context) MinimumFees() Coins { return c.Value(contextKeyMinimumFees).(Coins) }
|
||||
|
||||
func (c Context) WithMultiStore(ms MultiStore) Context { return c.withValue(contextKeyMultiStore, ms) }
|
||||
|
||||
func (c Context) WithBlockHeader(header abci.Header) Context {
|
||||
var _ proto.Message = &header // for cloning.
|
||||
return c.withValue(contextKeyBlockHeader, header)
|
||||
}
|
||||
|
||||
func (c Context) WithBlockHeight(height int64) Context {
|
||||
return c.withValue(contextKeyBlockHeight, height)
|
||||
}
|
||||
|
||||
func (c Context) WithConsensusParams(params *abci.ConsensusParams) Context {
|
||||
if params == nil {
|
||||
return c
|
||||
|
@ -187,20 +192,25 @@ func (c Context) WithConsensusParams(params *abci.ConsensusParams) Context {
|
|||
return c.withValue(contextKeyConsensusParams, params).
|
||||
WithGasMeter(NewGasMeter(params.TxSize.MaxGas))
|
||||
}
|
||||
func (c Context) WithChainID(chainID string) Context {
|
||||
return c.withValue(contextKeyChainID, chainID)
|
||||
}
|
||||
func (c Context) WithTxBytes(txBytes []byte) Context {
|
||||
return c.withValue(contextKeyTxBytes, txBytes)
|
||||
}
|
||||
func (c Context) WithLogger(logger log.Logger) Context {
|
||||
return c.withValue(contextKeyLogger, logger)
|
||||
}
|
||||
|
||||
func (c Context) WithChainID(chainID string) Context { return c.withValue(contextKeyChainID, chainID) }
|
||||
|
||||
func (c Context) WithTxBytes(txBytes []byte) Context { return c.withValue(contextKeyTxBytes, txBytes) }
|
||||
|
||||
func (c Context) WithLogger(logger log.Logger) Context { return c.withValue(contextKeyLogger, logger) }
|
||||
|
||||
func (c Context) WithSigningValidators(SigningValidators []abci.SigningValidator) Context {
|
||||
return c.withValue(contextKeySigningValidators, SigningValidators)
|
||||
}
|
||||
func (c Context) WithGasMeter(meter GasMeter) Context {
|
||||
return c.withValue(contextKeyGasMeter, meter)
|
||||
|
||||
func (c Context) WithGasMeter(meter GasMeter) Context { return c.withValue(contextKeyGasMeter, meter) }
|
||||
|
||||
func (c Context) WithIsCheckTx(isCheckTx bool) Context {
|
||||
return c.withValue(contextKeyIsCheckTx, isCheckTx)
|
||||
}
|
||||
|
||||
func (c Context) WithMinimumFees(minFees Coins) Context {
|
||||
return c.withValue(contextKeyMinimumFees, minFees)
|
||||
}
|
||||
|
||||
// Cache the multistore and return a new cached context. The cached context is
|
||||
|
|
|
@ -162,20 +162,23 @@ func TestContextWithCustom(t *testing.T) {
|
|||
logger := NewMockLogger()
|
||||
signvals := []abci.SigningValidator{{}}
|
||||
meter := types.NewGasMeter(10000)
|
||||
minFees := types.Coins{types.NewInt64Coin("feeCoin", 1)}
|
||||
|
||||
ctx = types.NewContext(nil, header, ischeck, logger).
|
||||
WithBlockHeight(height).
|
||||
WithChainID(chainid).
|
||||
WithTxBytes(txbytes).
|
||||
WithSigningValidators(signvals).
|
||||
WithGasMeter(meter)
|
||||
WithGasMeter(meter).
|
||||
WithMinimumFees(minFees)
|
||||
|
||||
require.Equal(t, header, ctx.BlockHeader())
|
||||
require.Equal(t, height, ctx.BlockHeight())
|
||||
require.Equal(t, chainid, ctx.ChainID())
|
||||
require.Equal(t, ischeck, ctx.IsCheckTx())
|
||||
require.Equal(t, txbytes, ctx.TxBytes())
|
||||
require.Equal(t, logger, ctx.Logger())
|
||||
require.Equal(t, signvals, ctx.SigningValidators())
|
||||
require.Equal(t, meter, ctx.GasMeter())
|
||||
|
||||
require.Equal(t, minFees, types.Coins{types.NewInt64Coin("feeCoin", 1)})
|
||||
}
|
||||
|
|
|
@ -56,6 +56,7 @@ const (
|
|||
CodeInvalidCoins CodeType = 11
|
||||
CodeOutOfGas CodeType = 12
|
||||
CodeMemoTooLarge CodeType = 13
|
||||
CodeInsufficientFee CodeType = 14
|
||||
|
||||
// CodespaceRoot is a codespace for error codes in this file only.
|
||||
// Notice that 0 is an "unset" codespace, which can be overridden with
|
||||
|
@ -101,6 +102,8 @@ func CodeToDefaultMsg(code CodeType) string {
|
|||
return "out of gas"
|
||||
case CodeMemoTooLarge:
|
||||
return "memo too large"
|
||||
case CodeInsufficientFee:
|
||||
return "insufficient fee"
|
||||
default:
|
||||
return unknownCodeMsg(code)
|
||||
}
|
||||
|
@ -150,6 +153,9 @@ func ErrOutOfGas(msg string) Error {
|
|||
func ErrMemoTooLarge(msg string) Error {
|
||||
return newErrorWithRootCodespace(CodeMemoTooLarge, msg)
|
||||
}
|
||||
func ErrInsufficientFee(msg string) Error {
|
||||
return newErrorWithRootCodespace(CodeInsufficientFee, msg)
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
// Error & sdkError
|
||||
|
|
|
@ -17,15 +17,10 @@ var (
|
|||
|
||||
// return version of CLI/node and commit hash
|
||||
func GetVersion() string {
|
||||
v := Version
|
||||
if GitCommit != "" {
|
||||
v = v + "-" + GitCommit
|
||||
}
|
||||
return v
|
||||
return Version
|
||||
}
|
||||
|
||||
// CMD
|
||||
func printVersion(cmd *cobra.Command, args []string) {
|
||||
v := GetVersion()
|
||||
fmt.Println(v)
|
||||
fmt.Println(GetVersion())
|
||||
}
|
||||
|
|
|
@ -1,11 +1,5 @@
|
|||
//nolint
|
||||
package version
|
||||
|
||||
const Maj = "0"
|
||||
const Min = "24"
|
||||
const Fix = "2"
|
||||
|
||||
const Version = "0.24.2"
|
||||
|
||||
// GitCommit set by build flags
|
||||
var GitCommit = ""
|
||||
var Version = ""
|
||||
|
|
|
@ -12,11 +12,12 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
deductFeesCost sdk.Gas = 10
|
||||
memoCostPerByte sdk.Gas = 1
|
||||
ed25519VerifyCost = 59
|
||||
secp256k1VerifyCost = 100
|
||||
maxMemoCharacters = 100
|
||||
deductFeesCost sdk.Gas = 10
|
||||
memoCostPerByte sdk.Gas = 1
|
||||
ed25519VerifyCost = 59
|
||||
secp256k1VerifyCost = 100
|
||||
maxMemoCharacters = 100
|
||||
feeDeductionGasFactor = 0.001
|
||||
)
|
||||
|
||||
// NewAnteHandler returns an AnteHandler that checks
|
||||
|
@ -94,8 +95,15 @@ func NewAnteHandler(am AccountMapper, fck FeeCollectionKeeper) sdk.AnteHandler {
|
|||
return newCtx, res, true
|
||||
}
|
||||
|
||||
requiredFees := adjustFeesByGas(ctx.MinimumFees(), fee.Gas)
|
||||
// fees must be greater than the minimum set by the validator adjusted by gas
|
||||
if ctx.IsCheckTx() && !simulate && !ctx.MinimumFees().IsZero() && fee.Amount.IsLT(requiredFees) {
|
||||
// validators reject any tx from the mempool with less than the minimum fee per gas * gas factor
|
||||
return newCtx, sdk.ErrInsufficientFee(fmt.Sprintf(
|
||||
"insufficient fee, got: %q required: %q", fee.Amount, requiredFees)).Result(), true
|
||||
}
|
||||
|
||||
// first sig pays the fees
|
||||
// TODO: Add min fees
|
||||
// Can this function be moved outside of the loop?
|
||||
if i == 0 && !fee.Amount.IsZero() {
|
||||
newCtx.GasMeter().ConsumeGas(deductFeesCost, "deductFees")
|
||||
|
@ -236,6 +244,15 @@ func consumeSignatureVerificationGas(meter sdk.GasMeter, pubkey crypto.PubKey) {
|
|||
}
|
||||
}
|
||||
|
||||
func adjustFeesByGas(fees sdk.Coins, gas int64) sdk.Coins {
|
||||
gasCost := int64(float64(gas) * feeDeductionGasFactor)
|
||||
gasFees := make(sdk.Coins, len(fees))
|
||||
for i := 0; i < len(fees); i++ {
|
||||
gasFees[i] = sdk.NewInt64Coin(fees[i].Denom, gasCost)
|
||||
}
|
||||
return fees.Plus(gasFees)
|
||||
}
|
||||
|
||||
// Deduct the fee from the account.
|
||||
// We could use the CoinKeeper (in addition to the AccountMapper,
|
||||
// because the CoinKeeper doesn't give us accounts), but it seems easier to do this.
|
||||
|
|
|
@ -627,3 +627,24 @@ func TestConsumeSignatureVerificationGas(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAdjustFeesByGas(t *testing.T) {
|
||||
type args struct {
|
||||
fee sdk.Coins
|
||||
gas int64
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want sdk.Coins
|
||||
}{
|
||||
{"nil coins", args{sdk.Coins{}, 10000}, sdk.Coins{}},
|
||||
{"nil coins", args{sdk.Coins{sdk.NewInt64Coin("A", 10), sdk.NewInt64Coin("B", 0)}, 10000}, sdk.Coins{sdk.NewInt64Coin("A", 20), sdk.NewInt64Coin("B", 10)}},
|
||||
{"negative coins", args{sdk.Coins{sdk.NewInt64Coin("A", -10), sdk.NewInt64Coin("B", 10)}, 10000}, sdk.Coins{sdk.NewInt64Coin("B", 20)}},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
require.True(t, tt.want.IsEqual(adjustFeesByGas(tt.args.fee, tt.args.gas)))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,3 +60,26 @@ func TestAccountMapperGetSet(t *testing.T) {
|
|||
require.NotNil(t, acc)
|
||||
require.Equal(t, newSequence, acc.GetSequence())
|
||||
}
|
||||
|
||||
func BenchmarkAccountMapperGetAccountFound(b *testing.B) {
|
||||
ms, capKey, _ := setupMultiStore()
|
||||
cdc := codec.New()
|
||||
RegisterBaseAccount(cdc)
|
||||
|
||||
// make context and mapper
|
||||
ctx := sdk.NewContext(ms, abci.Header{}, false, log.NewNopLogger())
|
||||
mapper := NewAccountMapper(cdc, capKey, ProtoBaseAccount)
|
||||
|
||||
// assumes b.N < 2**24
|
||||
for i := 0; i < b.N; i++ {
|
||||
arr := []byte{byte((i & 0xFF0000) >> 16), byte((i & 0xFF00) >> 8), byte(i & 0xFF)}
|
||||
addr := sdk.AccAddress(arr)
|
||||
acc := mapper.NewAccountWithAddress(ctx, addr)
|
||||
mapper.SetAccount(ctx, acc)
|
||||
}
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
arr := []byte{byte((i & 0xFF0000) >> 16), byte((i & 0xFF00) >> 8), byte(i & 0xFF)}
|
||||
mapper.GetAccount(ctx, sdk.AccAddress(arr))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,7 +69,7 @@ func SimulateSubmittingVotingAndSlashingForProposal(k gov.Keeper, sk stake.Keepe
|
|||
fops := make([]simulation.FutureOperation, numVotes+1)
|
||||
for i := 0; i < numVotes; i++ {
|
||||
whenVote := ctx.BlockHeight() + r.Int63n(votingPeriod)
|
||||
fops[i] = simulation.FutureOperation{int(whenVote), operationSimulateMsgVote(k, sk, keys[whoVotes[i]], proposalID)}
|
||||
fops[i] = simulation.FutureOperation{BlockHeight: int(whenVote), Op: operationSimulateMsgVote(k, sk, keys[whoVotes[i]], proposalID)}
|
||||
}
|
||||
// 3) Make an operation to ensure slashes were done correctly. (Really should be a future invariant)
|
||||
// TODO: Find a way to check if a validator was slashed other than just checking their balance a block
|
||||
|
|
|
@ -98,13 +98,14 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp,
|
|||
request := RandomRequestBeginBlock(r, validators, livenessTransitionMatrix, evidenceFraction, pastTimes, pastSigningValidators, event, header)
|
||||
// These are operations which have been queued by previous operations
|
||||
operationQueue := make(map[int][]Operation)
|
||||
timeOperationQueue := []FutureOperation{}
|
||||
var blockLogBuilders []*strings.Builder
|
||||
|
||||
if testingMode {
|
||||
blockLogBuilders = make([]*strings.Builder, numBlocks)
|
||||
}
|
||||
displayLogs := logPrinter(testingMode, blockLogBuilders)
|
||||
blockSimulator := createBlockSimulator(testingMode, tb, t, event, invariants, ops, operationQueue, numBlocks, displayLogs)
|
||||
blockSimulator := createBlockSimulator(testingMode, tb, t, event, invariants, ops, operationQueue, timeOperationQueue, numBlocks, displayLogs)
|
||||
if !testingMode {
|
||||
b.ResetTimer()
|
||||
} else {
|
||||
|
@ -139,9 +140,10 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp,
|
|||
|
||||
// Run queued operations. Ignores blocksize if blocksize is too small
|
||||
numQueuedOpsRan := runQueuedOperations(operationQueue, int(header.Height), tb, r, app, ctx, keys, logWriter, displayLogs, event)
|
||||
thisBlockSize -= numQueuedOpsRan
|
||||
numQueuedTimeOpsRan := runQueuedTimeOperations(timeOperationQueue, header.Time, tb, r, app, ctx, keys, logWriter, displayLogs, event)
|
||||
thisBlockSize = thisBlockSize - numQueuedOpsRan - numQueuedTimeOpsRan
|
||||
operations := blockSimulator(thisBlockSize, r, app, ctx, keys, header, logWriter)
|
||||
opCount += operations + numQueuedOpsRan
|
||||
opCount += operations + numQueuedOpsRan + numQueuedTimeOpsRan
|
||||
|
||||
res := app.EndBlock(abci.RequestEndBlock{})
|
||||
header.Height++
|
||||
|
@ -173,7 +175,7 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp,
|
|||
|
||||
// Returns a function to simulate blocks. Written like this to avoid constant parameters being passed everytime, to minimize
|
||||
// memory overhead
|
||||
func createBlockSimulator(testingMode bool, tb testing.TB, t *testing.T, event func(string), invariants []Invariant, ops []WeightedOperation, operationQueue map[int][]Operation, totalNumBlocks int, displayLogs func()) func(
|
||||
func createBlockSimulator(testingMode bool, tb testing.TB, t *testing.T, event func(string), invariants []Invariant, ops []WeightedOperation, operationQueue map[int][]Operation, timeOperationQueue []FutureOperation, totalNumBlocks int, displayLogs func()) func(
|
||||
blocksize int, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, privKeys []crypto.PrivKey, header abci.Header, logWriter func(string)) (opCount int) {
|
||||
totalOpWeight := 0
|
||||
for i := 0; i < len(ops); i++ {
|
||||
|
@ -200,7 +202,7 @@ func createBlockSimulator(testingMode bool, tb testing.TB, t *testing.T, event f
|
|||
}
|
||||
logWriter(logUpdate)
|
||||
|
||||
queueOperations(operationQueue, futureOps)
|
||||
queueOperations(operationQueue, timeOperationQueue, futureOps)
|
||||
if testingMode {
|
||||
if onOperation {
|
||||
assertAllInvariants(t, app, invariants, displayLogs)
|
||||
|
@ -239,15 +241,23 @@ func getBlockSize(r *rand.Rand, blockSize int) int {
|
|||
}
|
||||
|
||||
// adds all future operations into the operation queue.
|
||||
func queueOperations(queuedOperations map[int][]Operation, futureOperations []FutureOperation) {
|
||||
func queueOperations(queuedOperations map[int][]Operation, queuedTimeOperations []FutureOperation, futureOperations []FutureOperation) {
|
||||
if futureOperations == nil {
|
||||
return
|
||||
}
|
||||
for _, futureOp := range futureOperations {
|
||||
if val, ok := queuedOperations[futureOp.BlockHeight]; ok {
|
||||
queuedOperations[futureOp.BlockHeight] = append(val, futureOp.Op)
|
||||
if futureOp.BlockHeight != 0 {
|
||||
if val, ok := queuedOperations[futureOp.BlockHeight]; ok {
|
||||
queuedOperations[futureOp.BlockHeight] = append(val, futureOp.Op)
|
||||
} else {
|
||||
queuedOperations[futureOp.BlockHeight] = []Operation{futureOp.Op}
|
||||
}
|
||||
} else {
|
||||
queuedOperations[futureOp.BlockHeight] = []Operation{futureOp.Op}
|
||||
// TODO: Replace with proper sorted data structure, so don't have the copy entire slice
|
||||
index := sort.Search(len(queuedTimeOperations), func(i int) bool { return queuedTimeOperations[i].BlockTime.After(futureOp.BlockTime) })
|
||||
queuedTimeOperations = append(queuedTimeOperations, FutureOperation{})
|
||||
copy(queuedTimeOperations[index+1:], queuedTimeOperations[index:])
|
||||
queuedTimeOperations[index] = futureOp
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -274,6 +284,26 @@ func runQueuedOperations(queueOperations map[int][]Operation, height int, tb tes
|
|||
return 0
|
||||
}
|
||||
|
||||
func runQueuedTimeOperations(queueOperations []FutureOperation, currentTime time.Time, tb testing.TB, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
|
||||
privKeys []crypto.PrivKey, logWriter func(string), displayLogs func(), event func(string)) (numOpsRan int) {
|
||||
|
||||
numOpsRan = 0
|
||||
for len(queueOperations) > 0 && currentTime.After(queueOperations[0].BlockTime) {
|
||||
// For now, queued operations cannot queue more operations.
|
||||
// If a need arises for us to support queued messages to queue more messages, this can
|
||||
// be changed.
|
||||
logUpdate, _, err := queueOperations[0].Op(r, app, ctx, privKeys, event)
|
||||
logWriter(logUpdate)
|
||||
if err != nil {
|
||||
displayLogs()
|
||||
tb.FailNow()
|
||||
}
|
||||
queueOperations = queueOperations[1:]
|
||||
numOpsRan++
|
||||
}
|
||||
return numOpsRan
|
||||
}
|
||||
|
||||
func getKeys(validators map[string]mockValidator) []string {
|
||||
keys := make([]string, len(validators))
|
||||
i := 0
|
||||
|
|
|
@ -2,6 +2,7 @@ package simulation
|
|||
|
||||
import (
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
|
@ -41,10 +42,12 @@ type (
|
|||
|
||||
// FutureOperation is an operation which will be ran at the
|
||||
// beginning of the provided BlockHeight.
|
||||
// If both a BlockHeight and BlockTime are specified, it will use the BlockHeight.
|
||||
// In the (likely) event that multiple operations are queued at the same
|
||||
// block height, they will execute in a FIFO pattern.
|
||||
FutureOperation struct {
|
||||
BlockHeight int
|
||||
BlockTime time.Time
|
||||
Op Operation
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue