Introduce Wasmgovd (#189)
* Introduce wasmgovd; disable wasm proposals with wasmd * Update changelog * Setup wasmgov with permission Nobody * Review feedback
This commit is contained in:
@@ -37,6 +37,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
|
|||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
* (wasmd) [\#187](https://github.com/CosmWasm/wasmd/issues/187) Introduce wasmgovd binary
|
||||||
* (wasmd) [\#178](https://github.com/CosmWasm/wasmd/issues/178) Add cli support for wasm gov proposals
|
* (wasmd) [\#178](https://github.com/CosmWasm/wasmd/issues/178) Add cli support for wasm gov proposals
|
||||||
* (wasmd) [\#163](https://github.com/CosmWasm/wasmd/issues/163) Control who can instantiate code
|
* (wasmd) [\#163](https://github.com/CosmWasm/wasmd/issues/163) Control who can instantiate code
|
||||||
* (wasmd) [\#164](https://github.com/CosmWasm/wasmd/issues/164) Control who can upload code
|
* (wasmd) [\#164](https://github.com/CosmWasm/wasmd/issues/164) Control who can upload code
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ RUN BUILD_TAGS=muslc make build
|
|||||||
FROM alpine:3.12
|
FROM alpine:3.12
|
||||||
|
|
||||||
COPY --from=go-builder /code/build/wasmd /usr/bin/wasmd
|
COPY --from=go-builder /code/build/wasmd /usr/bin/wasmd
|
||||||
|
COPY --from=go-builder /code/build/wasmgovd /usr/bin/wasmgovd
|
||||||
COPY --from=go-builder /code/build/wasmcli /usr/bin/wasmcli
|
COPY --from=go-builder /code/build/wasmcli /usr/bin/wasmcli
|
||||||
|
|
||||||
COPY docker/* /opt/
|
COPY docker/* /opt/
|
||||||
|
|||||||
2
Makefile
2
Makefile
@@ -70,9 +70,11 @@ all: install lint test
|
|||||||
build: go.sum
|
build: go.sum
|
||||||
ifeq ($(OS),Windows_NT)
|
ifeq ($(OS),Windows_NT)
|
||||||
go build -mod=readonly $(BUILD_FLAGS) -o build/wasmd.exe ./cmd/wasmd
|
go build -mod=readonly $(BUILD_FLAGS) -o build/wasmd.exe ./cmd/wasmd
|
||||||
|
go build -mod=readonly $(BUILD_FLAGS) -o build/wasmgovd.exe ./cmd/wasmgovd
|
||||||
go build -mod=readonly $(BUILD_FLAGS) -o build/wasmcli.exe ./cmd/wasmcli
|
go build -mod=readonly $(BUILD_FLAGS) -o build/wasmcli.exe ./cmd/wasmcli
|
||||||
else
|
else
|
||||||
go build -mod=readonly $(BUILD_FLAGS) -o build/wasmd ./cmd/wasmd
|
go build -mod=readonly $(BUILD_FLAGS) -o build/wasmd ./cmd/wasmd
|
||||||
|
go build -mod=readonly $(BUILD_FLAGS) -o build/wasmgovd ./cmd/wasmgovd
|
||||||
go build -mod=readonly $(BUILD_FLAGS) -o build/wasmcli ./cmd/wasmcli
|
go build -mod=readonly $(BUILD_FLAGS) -o build/wasmcli ./cmd/wasmcli
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ func main() {
|
|||||||
cobra.EnableCommandSorting = false
|
cobra.EnableCommandSorting = false
|
||||||
rootCmd := &cobra.Command{
|
rootCmd := &cobra.Command{
|
||||||
Use: "wasmd",
|
Use: "wasmd",
|
||||||
Short: "Wasm Daemon (server)",
|
Short: "Wasm Daemon (server) with wasm gov proposals disabled\",",
|
||||||
PersistentPreRunE: server.PersistentPreRunEFn(ctx),
|
PersistentPreRunE: server.PersistentPreRunEFn(ctx),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,7 +86,7 @@ func newApp(logger log.Logger, db dbm.DB, traceStore io.Writer) abci.Application
|
|||||||
}
|
}
|
||||||
|
|
||||||
return app.NewWasmApp(logger, db, traceStore, true, invCheckPeriod,
|
return app.NewWasmApp(logger, db, traceStore, true, invCheckPeriod,
|
||||||
wasm.EnableAllProposals, skipUpgradeHeights,
|
wasm.DisableAllProposals, skipUpgradeHeights,
|
||||||
baseapp.SetPruning(store.NewPruningOptionsFromString(viper.GetString("pruning"))),
|
baseapp.SetPruning(store.NewPruningOptionsFromString(viper.GetString("pruning"))),
|
||||||
baseapp.SetMinGasPrices(viper.GetString(server.FlagMinGasPrices)),
|
baseapp.SetMinGasPrices(viper.GetString(server.FlagMinGasPrices)),
|
||||||
baseapp.SetHaltHeight(viper.GetUint64(server.FlagHaltHeight)),
|
baseapp.SetHaltHeight(viper.GetUint64(server.FlagHaltHeight)),
|
||||||
@@ -99,7 +99,7 @@ func exportAppStateAndTMValidators(
|
|||||||
) (json.RawMessage, []tmtypes.GenesisValidator, error) {
|
) (json.RawMessage, []tmtypes.GenesisValidator, error) {
|
||||||
|
|
||||||
if height != -1 {
|
if height != -1 {
|
||||||
gapp := app.NewWasmApp(logger, db, traceStore, false, uint(1), wasm.EnableAllProposals, nil)
|
gapp := app.NewWasmApp(logger, db, traceStore, false, uint(1), wasm.DisableAllProposals, nil)
|
||||||
err := gapp.LoadHeight(height)
|
err := gapp.LoadHeight(height)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
@@ -107,6 +107,6 @@ func exportAppStateAndTMValidators(
|
|||||||
return gapp.ExportAppStateAndValidators(forZeroHeight, jailWhiteList)
|
return gapp.ExportAppStateAndValidators(forZeroHeight, jailWhiteList)
|
||||||
}
|
}
|
||||||
|
|
||||||
gapp := app.NewWasmApp(logger, db, traceStore, true, uint(1), wasm.EnableAllProposals, nil)
|
gapp := app.NewWasmApp(logger, db, traceStore, true, uint(1), wasm.DisableAllProposals, nil)
|
||||||
return gapp.ExportAppStateAndValidators(forZeroHeight, jailWhiteList)
|
return gapp.ExportAppStateAndValidators(forZeroHeight, jailWhiteList)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ func replayTxs(rootDir string) error {
|
|||||||
fmt.Fprintln(os.Stderr, "Creating application")
|
fmt.Fprintln(os.Stderr, "Creating application")
|
||||||
gapp := app.NewWasmApp(
|
gapp := app.NewWasmApp(
|
||||||
// TODO: do we want to set skipUpgradeHieghts here?
|
// TODO: do we want to set skipUpgradeHieghts here?
|
||||||
ctx.Logger, appDB, traceStoreWriter, true, uint(1), wasm.EnableAllProposals, nil,
|
ctx.Logger, appDB, traceStoreWriter, true, uint(1), wasm.DisableAllProposals, nil,
|
||||||
baseapp.SetPruning(store.PruneEverything))
|
baseapp.SetPruning(store.PruneEverything))
|
||||||
|
|
||||||
// Genesis
|
// Genesis
|
||||||
|
|||||||
153
cmd/wasmgovd/genaccounts.go
Normal file
153
cmd/wasmgovd/genaccounts.go
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
|
||||||
|
"github.com/tendermint/tendermint/libs/cli"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||||
|
"github.com/cosmos/cosmos-sdk/codec"
|
||||||
|
"github.com/cosmos/cosmos-sdk/crypto/keys"
|
||||||
|
"github.com/cosmos/cosmos-sdk/server"
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||||
|
authexported "github.com/cosmos/cosmos-sdk/x/auth/exported"
|
||||||
|
authvesting "github.com/cosmos/cosmos-sdk/x/auth/vesting"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/genutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
flagClientHome = "home-client"
|
||||||
|
flagVestingStart = "vesting-start-time"
|
||||||
|
flagVestingEnd = "vesting-end-time"
|
||||||
|
flagVestingAmt = "vesting-amount"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AddGenesisAccountCmd returns add-genesis-account cobra Command.
|
||||||
|
func AddGenesisAccountCmd(
|
||||||
|
ctx *server.Context, cdc *codec.Codec, defaultNodeHome, defaultClientHome string,
|
||||||
|
) *cobra.Command {
|
||||||
|
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "add-genesis-account [address_or_key_name] [coin][,[coin]]",
|
||||||
|
Short: "Add a genesis account to genesis.json",
|
||||||
|
Long: `Add a genesis account to genesis.json. The provided account must specify
|
||||||
|
the account address or key name and a list of initial coins. If a key name is given,
|
||||||
|
the address will be looked up in the local Keybase. The list of initial tokens must
|
||||||
|
contain valid denominations. Accounts may optionally be supplied with vesting parameters.
|
||||||
|
`,
|
||||||
|
Args: cobra.ExactArgs(2),
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
config := ctx.Config
|
||||||
|
config.SetRoot(viper.GetString(cli.HomeFlag))
|
||||||
|
|
||||||
|
addr, err := sdk.AccAddressFromBech32(args[0])
|
||||||
|
inBuf := bufio.NewReader(cmd.InOrStdin())
|
||||||
|
if err != nil {
|
||||||
|
// attempt to lookup address from Keybase if no address was provided
|
||||||
|
kb, err := keys.NewKeyring(
|
||||||
|
sdk.KeyringServiceName(),
|
||||||
|
viper.GetString(flags.FlagKeyringBackend),
|
||||||
|
viper.GetString(flagClientHome),
|
||||||
|
inBuf,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
info, err := kb.Get(args[0])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get address from Keybase: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
addr = info.GetAddress()
|
||||||
|
}
|
||||||
|
|
||||||
|
coins, err := sdk.ParseCoins(args[1])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to parse coins: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
vestingStart := viper.GetInt64(flagVestingStart)
|
||||||
|
vestingEnd := viper.GetInt64(flagVestingEnd)
|
||||||
|
vestingAmt, err := sdk.ParseCoins(viper.GetString(flagVestingAmt))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to parse vesting amount: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// create concrete account type based on input parameters
|
||||||
|
var genAccount authexported.GenesisAccount
|
||||||
|
|
||||||
|
baseAccount := auth.NewBaseAccount(addr, coins.Sort(), nil, 0, 0)
|
||||||
|
if !vestingAmt.IsZero() {
|
||||||
|
baseVestingAccount, err := authvesting.NewBaseVestingAccount(baseAccount, vestingAmt.Sort(), vestingEnd)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create base vesting account: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case vestingStart != 0 && vestingEnd != 0:
|
||||||
|
genAccount = authvesting.NewContinuousVestingAccountRaw(baseVestingAccount, vestingStart)
|
||||||
|
|
||||||
|
case vestingEnd != 0:
|
||||||
|
genAccount = authvesting.NewDelayedVestingAccountRaw(baseVestingAccount)
|
||||||
|
|
||||||
|
default:
|
||||||
|
return errors.New("invalid vesting parameters; must supply start and end time or end time")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
genAccount = baseAccount
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := genAccount.Validate(); err != nil {
|
||||||
|
return fmt.Errorf("failed to validate new genesis account: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
genFile := config.GenesisFile()
|
||||||
|
appState, genDoc, err := genutil.GenesisStateFromGenFile(cdc, genFile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to unmarshal genesis state: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
authGenState := auth.GetGenesisStateFromAppState(cdc, appState)
|
||||||
|
|
||||||
|
if authGenState.Accounts.Contains(addr) {
|
||||||
|
return fmt.Errorf("cannot add account at existing address %s", addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the new account to the set of genesis accounts and sanitize the
|
||||||
|
// accounts afterwards.
|
||||||
|
authGenState.Accounts = append(authGenState.Accounts, genAccount)
|
||||||
|
authGenState.Accounts = auth.SanitizeGenesisAccounts(authGenState.Accounts)
|
||||||
|
|
||||||
|
authGenStateBz, err := cdc.MarshalJSON(authGenState)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to marshal auth genesis state: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
appState[auth.ModuleName] = authGenStateBz
|
||||||
|
|
||||||
|
appStateJSON, err := cdc.MarshalJSON(appState)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to marshal application genesis state: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
genDoc.AppState = appStateJSON
|
||||||
|
return genutil.ExportGenesisFile(genDoc, genFile)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.Flags().String(cli.HomeFlag, defaultNodeHome, "node's home directory")
|
||||||
|
cmd.Flags().String(flags.FlagKeyringBackend, flags.DefaultKeyringBackend, "Select keyring's backend (os|file|test)")
|
||||||
|
cmd.Flags().String(flagClientHome, defaultClientHome, "client's home directory")
|
||||||
|
cmd.Flags().String(flagVestingAmt, "", "amount of coins for vesting accounts")
|
||||||
|
cmd.Flags().Uint64(flagVestingStart, 0, "schedule start time (unix epoch) for vesting accounts")
|
||||||
|
cmd.Flags().Uint64(flagVestingEnd, 0, "schedule end time (unix epoch) for vesting accounts")
|
||||||
|
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
114
cmd/wasmgovd/main.go
Normal file
114
cmd/wasmgovd/main.go
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/CosmWasm/wasmd/app"
|
||||||
|
"github.com/CosmWasm/wasmd/x/wasm"
|
||||||
|
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||||
|
"github.com/cosmos/cosmos-sdk/client/debug"
|
||||||
|
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||||
|
"github.com/cosmos/cosmos-sdk/server"
|
||||||
|
"github.com/cosmos/cosmos-sdk/store"
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||||
|
genutilcli "github.com/cosmos/cosmos-sdk/x/genutil/client/cli"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/staking"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
abci "github.com/tendermint/tendermint/abci/types"
|
||||||
|
"github.com/tendermint/tendermint/libs/cli"
|
||||||
|
"github.com/tendermint/tendermint/libs/log"
|
||||||
|
tmtypes "github.com/tendermint/tendermint/types"
|
||||||
|
dbm "github.com/tendermint/tm-db"
|
||||||
|
)
|
||||||
|
|
||||||
|
const flagInvCheckPeriod = "inv-check-period"
|
||||||
|
|
||||||
|
var invCheckPeriod uint
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
cdc := app.MakeCodec()
|
||||||
|
|
||||||
|
config := sdk.GetConfig()
|
||||||
|
config.SetBech32PrefixForAccount(sdk.Bech32PrefixAccAddr, sdk.Bech32PrefixAccPub)
|
||||||
|
config.SetBech32PrefixForValidator(sdk.Bech32PrefixValAddr, sdk.Bech32PrefixValPub)
|
||||||
|
config.SetBech32PrefixForConsensusNode(sdk.Bech32PrefixConsAddr, sdk.Bech32PrefixConsPub)
|
||||||
|
config.Seal()
|
||||||
|
|
||||||
|
homeDir := os.ExpandEnv("$HOME/.wasmgovd")
|
||||||
|
|
||||||
|
ctx := server.NewDefaultContext()
|
||||||
|
cobra.EnableCommandSorting = false
|
||||||
|
rootCmd := &cobra.Command{
|
||||||
|
Use: "wasmgovd",
|
||||||
|
Short: "Wasm Daemon (server) with wasm gov proposals enabled\",",
|
||||||
|
PersistentPreRunE: server.PersistentPreRunEFn(ctx),
|
||||||
|
}
|
||||||
|
rootCmd.AddCommand(genutilcli.InitCmd(ctx, cdc, app.ModuleBasics, homeDir))
|
||||||
|
rootCmd.AddCommand(genutilcli.CollectGenTxsCmd(ctx, cdc, auth.GenesisAccountIterator{}, homeDir))
|
||||||
|
rootCmd.AddCommand(genutilcli.MigrateGenesisCmd(ctx, cdc))
|
||||||
|
rootCmd.AddCommand(
|
||||||
|
genutilcli.GenTxCmd(
|
||||||
|
ctx, cdc, app.ModuleBasics, staking.AppModuleBasic{},
|
||||||
|
auth.GenesisAccountIterator{}, homeDir, app.DefaultCLIHome,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
rootCmd.AddCommand(genutilcli.ValidateGenesisCmd(ctx, cdc, app.ModuleBasics))
|
||||||
|
rootCmd.AddCommand(AddGenesisAccountCmd(ctx, cdc, homeDir, app.DefaultCLIHome))
|
||||||
|
rootCmd.AddCommand(flags.NewCompletionCmd(rootCmd, true))
|
||||||
|
// rootCmd.AddCommand(testnetCmd(ctx, cdc, app.ModuleBasics, auth.GenesisAccountIterator{}))
|
||||||
|
rootCmd.AddCommand(replayCmd())
|
||||||
|
rootCmd.AddCommand(debug.Cmd(cdc))
|
||||||
|
|
||||||
|
server.AddCommands(ctx, cdc, rootCmd, newApp, exportAppStateAndTMValidators)
|
||||||
|
|
||||||
|
// prepare and add flags
|
||||||
|
executor := cli.PrepareBaseCmd(rootCmd, "WM", homeDir)
|
||||||
|
rootCmd.PersistentFlags().UintVar(&invCheckPeriod, flagInvCheckPeriod,
|
||||||
|
0, "Assert registered invariants every N blocks")
|
||||||
|
err := executor.Execute()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newApp(logger log.Logger, db dbm.DB, traceStore io.Writer) abci.Application {
|
||||||
|
var cache sdk.MultiStorePersistentCache
|
||||||
|
|
||||||
|
if viper.GetBool(server.FlagInterBlockCache) {
|
||||||
|
cache = store.NewCommitKVStoreCacheManager()
|
||||||
|
}
|
||||||
|
|
||||||
|
skipUpgradeHeights := make(map[int64]bool)
|
||||||
|
for _, h := range viper.GetIntSlice(server.FlagUnsafeSkipUpgrades) {
|
||||||
|
skipUpgradeHeights[int64(h)] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return app.NewWasmApp(logger, db, traceStore, true, invCheckPeriod,
|
||||||
|
wasm.EnableAllProposals, skipUpgradeHeights,
|
||||||
|
baseapp.SetPruning(store.NewPruningOptionsFromString(viper.GetString("pruning"))),
|
||||||
|
baseapp.SetMinGasPrices(viper.GetString(server.FlagMinGasPrices)),
|
||||||
|
baseapp.SetHaltHeight(viper.GetUint64(server.FlagHaltHeight)),
|
||||||
|
baseapp.SetHaltTime(viper.GetUint64(server.FlagHaltTime)),
|
||||||
|
baseapp.SetInterBlockCache(cache))
|
||||||
|
}
|
||||||
|
|
||||||
|
func exportAppStateAndTMValidators(
|
||||||
|
logger log.Logger, db dbm.DB, traceStore io.Writer, height int64, forZeroHeight bool, jailWhiteList []string,
|
||||||
|
) (json.RawMessage, []tmtypes.GenesisValidator, error) {
|
||||||
|
|
||||||
|
if height != -1 {
|
||||||
|
gapp := app.NewWasmApp(logger, db, traceStore, false, uint(1), wasm.EnableAllProposals, nil)
|
||||||
|
err := gapp.LoadHeight(height)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return gapp.ExportAppStateAndValidators(forZeroHeight, jailWhiteList)
|
||||||
|
}
|
||||||
|
|
||||||
|
gapp := app.NewWasmApp(logger, db, traceStore, true, uint(1), wasm.EnableAllProposals, nil)
|
||||||
|
return gapp.ExportAppStateAndValidators(forZeroHeight, jailWhiteList)
|
||||||
|
}
|
||||||
191
cmd/wasmgovd/replay.go
Normal file
191
cmd/wasmgovd/replay.go
Normal file
@@ -0,0 +1,191 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/CosmWasm/wasmd/app"
|
||||||
|
"github.com/CosmWasm/wasmd/x/wasm"
|
||||||
|
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||||
|
"github.com/cosmos/cosmos-sdk/server"
|
||||||
|
"github.com/cosmos/cosmos-sdk/store"
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
cpm "github.com/otiai10/copy"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
abci "github.com/tendermint/tendermint/abci/types"
|
||||||
|
tmos "github.com/tendermint/tendermint/libs/os"
|
||||||
|
"github.com/tendermint/tendermint/proxy"
|
||||||
|
tmsm "github.com/tendermint/tendermint/state"
|
||||||
|
tmstore "github.com/tendermint/tendermint/store"
|
||||||
|
tm "github.com/tendermint/tendermint/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func replayCmd() *cobra.Command {
|
||||||
|
return &cobra.Command{
|
||||||
|
Use: "replay <root-dir>",
|
||||||
|
Short: "Replay gaia transactions",
|
||||||
|
RunE: func(_ *cobra.Command, args []string) error {
|
||||||
|
return replayTxs(args[0])
|
||||||
|
},
|
||||||
|
Args: cobra.ExactArgs(1),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func replayTxs(rootDir string) error {
|
||||||
|
|
||||||
|
if false {
|
||||||
|
// Copy the rootDir to a new directory, to preserve the old one.
|
||||||
|
fmt.Fprintln(os.Stderr, "Copying rootdir over")
|
||||||
|
oldRootDir := rootDir
|
||||||
|
rootDir = oldRootDir + "_replay"
|
||||||
|
if tmos.FileExists(rootDir) {
|
||||||
|
tmos.Exit(fmt.Sprintf("temporary copy dir %v already exists", rootDir))
|
||||||
|
}
|
||||||
|
if err := cpm.Copy(oldRootDir, rootDir); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
configDir := filepath.Join(rootDir, "config")
|
||||||
|
dataDir := filepath.Join(rootDir, "data")
|
||||||
|
ctx := server.NewDefaultContext()
|
||||||
|
|
||||||
|
// App DB
|
||||||
|
// appDB := dbm.NewMemDB()
|
||||||
|
fmt.Fprintln(os.Stderr, "Opening app database")
|
||||||
|
appDB, err := sdk.NewLevelDB("application", dataDir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TM DB
|
||||||
|
// tmDB := dbm.NewMemDB()
|
||||||
|
fmt.Fprintln(os.Stderr, "Opening tendermint state database")
|
||||||
|
tmDB, err := sdk.NewLevelDB("state", dataDir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Blockchain DB
|
||||||
|
fmt.Fprintln(os.Stderr, "Opening blockstore database")
|
||||||
|
bcDB, err := sdk.NewLevelDB("blockstore", dataDir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TraceStore
|
||||||
|
var traceStoreWriter io.Writer
|
||||||
|
var traceStoreDir = filepath.Join(dataDir, "trace.log")
|
||||||
|
traceStoreWriter, err = os.OpenFile(
|
||||||
|
traceStoreDir,
|
||||||
|
os.O_WRONLY|os.O_APPEND|os.O_CREATE,
|
||||||
|
0666,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Application
|
||||||
|
fmt.Fprintln(os.Stderr, "Creating application")
|
||||||
|
gapp := app.NewWasmApp(
|
||||||
|
// TODO: do we want to set skipUpgradeHieghts here?
|
||||||
|
ctx.Logger, appDB, traceStoreWriter, true, uint(1), wasm.EnableAllProposals, nil,
|
||||||
|
baseapp.SetPruning(store.PruneEverything))
|
||||||
|
|
||||||
|
// Genesis
|
||||||
|
var genDocPath = filepath.Join(configDir, "genesis.json")
|
||||||
|
genDoc, err := tm.GenesisDocFromFile(genDocPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
genState, err := tmsm.MakeGenesisState(genDoc)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// tmsm.SaveState(tmDB, genState)
|
||||||
|
|
||||||
|
cc := proxy.NewLocalClientCreator(gapp)
|
||||||
|
proxyApp := proxy.NewAppConns(cc)
|
||||||
|
err = proxyApp.Start()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
err = proxyApp.Stop()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
state := tmsm.LoadState(tmDB)
|
||||||
|
if state.LastBlockHeight == 0 {
|
||||||
|
// Send InitChain msg
|
||||||
|
fmt.Fprintln(os.Stderr, "Sending InitChain msg")
|
||||||
|
validators := tm.TM2PB.ValidatorUpdates(genState.Validators)
|
||||||
|
csParams := tm.TM2PB.ConsensusParams(genDoc.ConsensusParams)
|
||||||
|
req := abci.RequestInitChain{
|
||||||
|
Time: genDoc.GenesisTime,
|
||||||
|
ChainId: genDoc.ChainID,
|
||||||
|
ConsensusParams: csParams,
|
||||||
|
Validators: validators,
|
||||||
|
AppStateBytes: genDoc.AppState,
|
||||||
|
}
|
||||||
|
res, err := proxyApp.Consensus().InitChainSync(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
newValidatorz, err := tm.PB2TM.ValidatorUpdates(res.Validators)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
newValidators := tm.NewValidatorSet(newValidatorz)
|
||||||
|
|
||||||
|
// Take the genesis state.
|
||||||
|
state = genState
|
||||||
|
state.Validators = newValidators
|
||||||
|
state.NextValidators = newValidators
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create executor
|
||||||
|
fmt.Fprintln(os.Stderr, "Creating block executor")
|
||||||
|
blockExec := tmsm.NewBlockExecutor(tmDB, ctx.Logger, proxyApp.Consensus(), nil, tmsm.MockEvidencePool{})
|
||||||
|
|
||||||
|
// Create block store
|
||||||
|
fmt.Fprintln(os.Stderr, "Creating block store")
|
||||||
|
blockStore := tmstore.NewBlockStore(bcDB)
|
||||||
|
|
||||||
|
tz := []time.Duration{0, 0, 0}
|
||||||
|
for i := int(state.LastBlockHeight) + 1; ; i++ {
|
||||||
|
fmt.Fprintln(os.Stderr, "Running block ", i)
|
||||||
|
t1 := time.Now()
|
||||||
|
|
||||||
|
// Apply block
|
||||||
|
fmt.Printf("loading and applying block %d\n", i)
|
||||||
|
blockmeta := blockStore.LoadBlockMeta(int64(i))
|
||||||
|
if blockmeta == nil {
|
||||||
|
fmt.Printf("Couldn't find block meta %d... done?\n", i)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
block := blockStore.LoadBlock(int64(i))
|
||||||
|
if block == nil {
|
||||||
|
return fmt.Errorf("couldn't find block %d", i)
|
||||||
|
}
|
||||||
|
|
||||||
|
t2 := time.Now()
|
||||||
|
|
||||||
|
state, err = blockExec.ApplyBlock(state, blockmeta.BlockID, block)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
t3 := time.Now()
|
||||||
|
tz[0] += t2.Sub(t1)
|
||||||
|
tz[1] += t3.Sub(t2)
|
||||||
|
|
||||||
|
fmt.Fprintf(os.Stderr, "new app hash: %X\n", state.AppHash)
|
||||||
|
fmt.Fprintln(os.Stderr, tz)
|
||||||
|
}
|
||||||
|
}
|
||||||
374
cmd/wasmgovd/testnet.go
Normal file
374
cmd/wasmgovd/testnet.go
Normal file
@@ -0,0 +1,374 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
// DONTCOVER
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
tmconfig "github.com/tendermint/tendermint/config"
|
||||||
|
"github.com/tendermint/tendermint/crypto"
|
||||||
|
tmos "github.com/tendermint/tendermint/libs/os"
|
||||||
|
tmrand "github.com/tendermint/tendermint/libs/rand"
|
||||||
|
"github.com/tendermint/tendermint/types"
|
||||||
|
tmtime "github.com/tendermint/tendermint/types/time"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||||
|
clientkeys "github.com/cosmos/cosmos-sdk/client/keys"
|
||||||
|
"github.com/cosmos/cosmos-sdk/codec"
|
||||||
|
"github.com/cosmos/cosmos-sdk/crypto/keys"
|
||||||
|
"github.com/cosmos/cosmos-sdk/server"
|
||||||
|
srvconfig "github.com/cosmos/cosmos-sdk/server/config"
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
"github.com/cosmos/cosmos-sdk/types/module"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||||
|
authexported "github.com/cosmos/cosmos-sdk/x/auth/exported"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/genutil"
|
||||||
|
genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/staking"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
flagNodeDirPrefix = "node-dir-prefix"
|
||||||
|
flagNumValidators = "v"
|
||||||
|
flagOutputDir = "output-dir"
|
||||||
|
flagNodeDaemonHome = "node-daemon-home"
|
||||||
|
flagNodeCLIHome = "node-cli-home"
|
||||||
|
flagStartingIPAddress = "starting-ip-address"
|
||||||
|
)
|
||||||
|
|
||||||
|
// get cmd to initialize all files for tendermint testnet and application
|
||||||
|
func testnetCmd(ctx *server.Context, cdc *codec.Codec,
|
||||||
|
mbm module.BasicManager, genAccIterator genutiltypes.GenesisAccountsIterator,
|
||||||
|
) *cobra.Command {
|
||||||
|
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "testnet",
|
||||||
|
Short: "Initialize files for a Wasmd testnet",
|
||||||
|
Long: `testnet will create "v" number of directories and populate each with
|
||||||
|
necessary files (private validator, genesis, config, etc.).
|
||||||
|
|
||||||
|
Note, strict routability for addresses is turned off in the config file.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
wasmd testnet --v 4 --output-dir ./output --starting-ip-address 192.168.10.2
|
||||||
|
`,
|
||||||
|
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||||
|
config := ctx.Config
|
||||||
|
|
||||||
|
outputDir := viper.GetString(flagOutputDir)
|
||||||
|
chainID := viper.GetString(flags.FlagChainID)
|
||||||
|
minGasPrices := viper.GetString(server.FlagMinGasPrices)
|
||||||
|
nodeDirPrefix := viper.GetString(flagNodeDirPrefix)
|
||||||
|
nodeDaemonHome := viper.GetString(flagNodeDaemonHome)
|
||||||
|
nodeCLIHome := viper.GetString(flagNodeCLIHome)
|
||||||
|
startingIPAddress := viper.GetString(flagStartingIPAddress)
|
||||||
|
numValidators := viper.GetInt(flagNumValidators)
|
||||||
|
|
||||||
|
return InitTestnet(cmd, config, cdc, mbm, genAccIterator, outputDir, chainID,
|
||||||
|
minGasPrices, nodeDirPrefix, nodeDaemonHome, nodeCLIHome, startingIPAddress, numValidators)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.Flags().Int(flagNumValidators, 4,
|
||||||
|
"Number of validators to initialize the testnet with")
|
||||||
|
cmd.Flags().StringP(flagOutputDir, "o", "./mytestnet",
|
||||||
|
"Directory to store initialization data for the testnet")
|
||||||
|
cmd.Flags().String(flagNodeDirPrefix, "node",
|
||||||
|
"Prefix the directory name for each node with (node results in node0, node1, ...)")
|
||||||
|
cmd.Flags().String(flagNodeDaemonHome, "wasmd",
|
||||||
|
"Home directory of the node's daemon configuration")
|
||||||
|
cmd.Flags().String(flagNodeCLIHome, "wasmcli",
|
||||||
|
"Home directory of the node's cli configuration")
|
||||||
|
cmd.Flags().String(flagStartingIPAddress, "192.168.0.1",
|
||||||
|
"Starting IP address (192.168.0.1 results in persistent peers list ID0@192.168.0.1:46656, ID1@192.168.0.2:46656, ...)")
|
||||||
|
cmd.Flags().String(
|
||||||
|
flags.FlagChainID, "", "genesis file chain-id, if left blank will be randomly created")
|
||||||
|
cmd.Flags().String(
|
||||||
|
server.FlagMinGasPrices, fmt.Sprintf("0.000006%s", sdk.DefaultBondDenom),
|
||||||
|
"Minimum gas prices to accept for transactions; All fees in a tx must meet this minimum (e.g. 0.01photino,0.001stake)")
|
||||||
|
cmd.Flags().String(flags.FlagKeyringBackend, flags.DefaultKeyringBackend, "Select keyring's backend (os|file|test)")
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
const nodeDirPerm = 0755
|
||||||
|
|
||||||
|
// Initialize the testnet
|
||||||
|
func InitTestnet(cmd *cobra.Command, config *tmconfig.Config, cdc *codec.Codec,
|
||||||
|
mbm module.BasicManager, genAccIterator genutiltypes.GenesisAccountsIterator,
|
||||||
|
outputDir, chainID, minGasPrices, nodeDirPrefix, nodeDaemonHome,
|
||||||
|
nodeCLIHome, startingIPAddress string, numValidators int) error {
|
||||||
|
|
||||||
|
if chainID == "" {
|
||||||
|
chainID = "chain-" + tmrand.Str(6)
|
||||||
|
}
|
||||||
|
|
||||||
|
monikers := make([]string, numValidators)
|
||||||
|
nodeIDs := make([]string, numValidators)
|
||||||
|
valPubKeys := make([]crypto.PubKey, numValidators)
|
||||||
|
|
||||||
|
wasmConfig := srvconfig.DefaultConfig()
|
||||||
|
wasmConfig.MinGasPrices = minGasPrices
|
||||||
|
|
||||||
|
//nolint:prealloc
|
||||||
|
var (
|
||||||
|
genAccounts []authexported.GenesisAccount
|
||||||
|
genFiles []string
|
||||||
|
)
|
||||||
|
|
||||||
|
inBuf := bufio.NewReader(cmd.InOrStdin())
|
||||||
|
// generate private keys, node IDs, and initial transactions
|
||||||
|
for i := 0; i < numValidators; i++ {
|
||||||
|
nodeDirName := fmt.Sprintf("%s%d", nodeDirPrefix, i)
|
||||||
|
nodeDir := filepath.Join(outputDir, nodeDirName, nodeDaemonHome)
|
||||||
|
clientDir := filepath.Join(outputDir, nodeDirName, nodeCLIHome)
|
||||||
|
gentxsDir := filepath.Join(outputDir, "gentxs")
|
||||||
|
|
||||||
|
config.SetRoot(nodeDir)
|
||||||
|
config.RPC.ListenAddress = "tcp://0.0.0.0:26657"
|
||||||
|
|
||||||
|
if err := os.MkdirAll(filepath.Join(nodeDir, "config"), nodeDirPerm); err != nil {
|
||||||
|
_ = os.RemoveAll(outputDir)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.MkdirAll(clientDir, nodeDirPerm); err != nil {
|
||||||
|
_ = os.RemoveAll(outputDir)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
monikers = append(monikers, nodeDirName)
|
||||||
|
config.Moniker = nodeDirName
|
||||||
|
|
||||||
|
ip, err := getIP(i, startingIPAddress)
|
||||||
|
if err != nil {
|
||||||
|
_ = os.RemoveAll(outputDir)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeIDs[i], valPubKeys[i], err = genutil.InitializeNodeValidatorFiles(config)
|
||||||
|
if err != nil {
|
||||||
|
_ = os.RemoveAll(outputDir)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
memo := fmt.Sprintf("%s@%s:26656", nodeIDs[i], ip)
|
||||||
|
genFiles = append(genFiles, config.GenesisFile())
|
||||||
|
|
||||||
|
kb, err := keys.NewKeyring(
|
||||||
|
sdk.KeyringServiceName(),
|
||||||
|
viper.GetString(flags.FlagKeyringBackend),
|
||||||
|
viper.GetString(flagClientHome),
|
||||||
|
inBuf,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
keyPass := clientkeys.DefaultKeyPass
|
||||||
|
addr, secret, err := server.GenerateSaveCoinKey(kb, nodeDirName, keyPass, true)
|
||||||
|
if err != nil {
|
||||||
|
_ = os.RemoveAll(outputDir)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
info := map[string]string{"secret": secret}
|
||||||
|
|
||||||
|
cliPrint, err := json.Marshal(info)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// save private key seed words
|
||||||
|
if err := writeFile(fmt.Sprintf("%v.json", "key_seed"), clientDir, cliPrint); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
accTokens := sdk.TokensFromConsensusPower(1000)
|
||||||
|
accStakingTokens := sdk.TokensFromConsensusPower(500)
|
||||||
|
coins := sdk.Coins{
|
||||||
|
sdk.NewCoin(fmt.Sprintf("%stoken", nodeDirName), accTokens),
|
||||||
|
sdk.NewCoin(sdk.DefaultBondDenom, accStakingTokens),
|
||||||
|
}
|
||||||
|
genAccounts = append(genAccounts, auth.NewBaseAccount(addr, coins.Sort(), nil, 0, 0))
|
||||||
|
|
||||||
|
valTokens := sdk.TokensFromConsensusPower(100)
|
||||||
|
msg := staking.NewMsgCreateValidator(
|
||||||
|
sdk.ValAddress(addr),
|
||||||
|
valPubKeys[i],
|
||||||
|
sdk.NewCoin(sdk.DefaultBondDenom, valTokens),
|
||||||
|
staking.NewDescription(nodeDirName, "", "", "", ""),
|
||||||
|
staking.NewCommissionRates(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()),
|
||||||
|
sdk.OneInt(),
|
||||||
|
)
|
||||||
|
|
||||||
|
tx := auth.NewStdTx([]sdk.Msg{msg}, auth.StdFee{}, []auth.StdSignature{}, memo)
|
||||||
|
txBldr := auth.NewTxBuilderFromCLI(inBuf).WithChainID(chainID).WithMemo(memo).WithKeybase(kb)
|
||||||
|
|
||||||
|
signedTx, err := txBldr.SignStdTx(nodeDirName, clientkeys.DefaultKeyPass, tx, false)
|
||||||
|
if err != nil {
|
||||||
|
_ = os.RemoveAll(outputDir)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
txBytes, err := cdc.MarshalJSON(signedTx)
|
||||||
|
if err != nil {
|
||||||
|
_ = os.RemoveAll(outputDir)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// gather gentxs folder
|
||||||
|
if err := writeFile(fmt.Sprintf("%v.json", nodeDirName), gentxsDir, txBytes); err != nil {
|
||||||
|
_ = os.RemoveAll(outputDir)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Rename config file to server.toml as it's not particular to Gaia
|
||||||
|
// (REF: https://github.com/cosmos/cosmos-sdk/issues/4125).
|
||||||
|
wasmConfigFilePath := filepath.Join(nodeDir, "config/wasmd.toml")
|
||||||
|
srvconfig.WriteConfigFile(wasmConfigFilePath, wasmConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := initGenFiles(cdc, mbm, chainID, genAccounts, genFiles, numValidators); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err := collectGenFiles(
|
||||||
|
cdc, config, chainID, monikers, nodeIDs, valPubKeys, numValidators,
|
||||||
|
outputDir, nodeDirPrefix, nodeDaemonHome, genAccIterator,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.PrintErrf("Successfully initialized %d node directories\n", numValidators)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func initGenFiles(
|
||||||
|
cdc *codec.Codec, mbm module.BasicManager, chainID string,
|
||||||
|
genAccounts []authexported.GenesisAccount, genFiles []string, numValidators int,
|
||||||
|
) error {
|
||||||
|
|
||||||
|
appGenState := mbm.DefaultGenesis()
|
||||||
|
|
||||||
|
// set the accounts in the genesis state
|
||||||
|
authDataBz := appGenState[auth.ModuleName]
|
||||||
|
var authGenState auth.GenesisState
|
||||||
|
cdc.MustUnmarshalJSON(authDataBz, &authGenState)
|
||||||
|
authGenState.Accounts = genAccounts
|
||||||
|
appGenState[auth.ModuleName] = cdc.MustMarshalJSON(authGenState)
|
||||||
|
|
||||||
|
appGenStateJSON, err := codec.MarshalJSONIndent(cdc, appGenState)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
genDoc := types.GenesisDoc{
|
||||||
|
ChainID: chainID,
|
||||||
|
AppState: appGenStateJSON,
|
||||||
|
Validators: nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate empty genesis files for each validator and save
|
||||||
|
for i := 0; i < numValidators; i++ {
|
||||||
|
if err := genDoc.SaveAs(genFiles[i]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func collectGenFiles(
|
||||||
|
cdc *codec.Codec, config *tmconfig.Config, chainID string,
|
||||||
|
monikers, nodeIDs []string, valPubKeys []crypto.PubKey,
|
||||||
|
numValidators int, outputDir, nodeDirPrefix, nodeDaemonHome string,
|
||||||
|
genAccIterator genutiltypes.GenesisAccountsIterator) error {
|
||||||
|
|
||||||
|
var appState json.RawMessage
|
||||||
|
genTime := tmtime.Now()
|
||||||
|
|
||||||
|
for i := 0; i < numValidators; i++ {
|
||||||
|
nodeDirName := fmt.Sprintf("%s%d", nodeDirPrefix, i)
|
||||||
|
nodeDir := filepath.Join(outputDir, nodeDirName, nodeDaemonHome)
|
||||||
|
gentxsDir := filepath.Join(outputDir, "gentxs")
|
||||||
|
moniker := monikers[i]
|
||||||
|
config.Moniker = nodeDirName
|
||||||
|
|
||||||
|
config.SetRoot(nodeDir)
|
||||||
|
|
||||||
|
nodeID, valPubKey := nodeIDs[i], valPubKeys[i]
|
||||||
|
initCfg := genutil.NewInitConfig(chainID, gentxsDir, moniker, nodeID, valPubKey)
|
||||||
|
|
||||||
|
genDoc, err := types.GenesisDocFromFile(config.GenesisFile())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeAppState, err := genutil.GenAppStateFromConfig(cdc, config, initCfg, *genDoc, genAccIterator)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if appState == nil {
|
||||||
|
// set the canonical application state (they should not differ)
|
||||||
|
appState = nodeAppState
|
||||||
|
}
|
||||||
|
|
||||||
|
genFile := config.GenesisFile()
|
||||||
|
|
||||||
|
// overwrite each validator's genesis file to have a canonical genesis time
|
||||||
|
if err := genutil.ExportGenesisFileWithTime(genFile, chainID, nil, appState, genTime); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getIP(i int, startingIPAddr string) (ip string, err error) {
|
||||||
|
if len(startingIPAddr) == 0 {
|
||||||
|
ip, err = server.ExternalIP()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return ip, nil
|
||||||
|
}
|
||||||
|
return calculateIP(startingIPAddr, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
func calculateIP(ip string, i int) (string, error) {
|
||||||
|
ipv4 := net.ParseIP(ip).To4()
|
||||||
|
if ipv4 == nil {
|
||||||
|
return "", fmt.Errorf("%v: non ipv4 address", ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
ipv4[3]++
|
||||||
|
}
|
||||||
|
|
||||||
|
return ipv4.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeFile(name string, dir string, contents []byte) error {
|
||||||
|
writePath := filepath.Join(dir)
|
||||||
|
file := filepath.Join(writePath, name)
|
||||||
|
|
||||||
|
err := tmos.EnsureDir(writePath, 0700)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tmos.WriteFile(file, contents, 0600)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
10
docker/run_wasmgovd.sh
Executable file
10
docker/run_wasmgovd.sh
Executable file
@@ -0,0 +1,10 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
if test -n "$1"; then
|
||||||
|
# need -R not -r to copy hidden files
|
||||||
|
cp -R "$1/.wasmd" /root
|
||||||
|
cp -R "$1/.wasmcli" /root
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p /root/log
|
||||||
|
wasmgovd start --rpc.laddr tcp://0.0.0.0:26657 --trace
|
||||||
24
docker/setup_wasmgovd.sh
Executable file
24
docker/setup_wasmgovd.sh
Executable file
@@ -0,0 +1,24 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
#set -o errexit -o nounset -o pipefail
|
||||||
|
|
||||||
|
PASSWORD=${PASSWORD:-1234567890}
|
||||||
|
STAKE=${STAKE_TOKEN:-ustake}
|
||||||
|
FEE=${FEE_TOKEN:-ucosm}
|
||||||
|
|
||||||
|
wasmgovd init --chain-id=testing testing
|
||||||
|
sed -i 's/permission": "Everybody"/permission": "Nobody"/' "$HOME"/.wasmgovd/config/genesis.json
|
||||||
|
# staking/governance token is hardcoded in config, change this
|
||||||
|
sed -i "s/\"stake\"/\"$STAKE\"/" "$HOME"/.wasmgovd/config/genesis.json
|
||||||
|
if ! wasmcli keys show validator; then
|
||||||
|
(echo "$PASSWORD"; echo "$PASSWORD") | wasmcli keys add validator
|
||||||
|
fi
|
||||||
|
# hardcode the validator account for this instance
|
||||||
|
echo "$PASSWORD" | wasmgovd add-genesis-account validator "1000000000$STAKE,1000000000$FEE"
|
||||||
|
# (optionally) add a few more genesis accounts
|
||||||
|
for addr in "$@"; do
|
||||||
|
echo $addr
|
||||||
|
wasmgovd add-genesis-account "$addr" "1000000000$STAKE,1000000000$FEE"
|
||||||
|
done
|
||||||
|
# submit a genesis validator tx
|
||||||
|
(echo "$PASSWORD"; echo "$PASSWORD"; echo "$PASSWORD") | wasmgovd gentx --name validator --amount "250000000$STAKE"
|
||||||
|
wasmgovd collect-gentxs
|
||||||
File diff suppressed because one or more lines are too long
@@ -306,7 +306,7 @@ func TestInstantiate(t *testing.T) {
|
|||||||
require.Equal(t, "cosmos18vd8fpwxzck93qlwghaj6arh4p7c5n89uzcee5", addr.String())
|
require.Equal(t, "cosmos18vd8fpwxzck93qlwghaj6arh4p7c5n89uzcee5", addr.String())
|
||||||
|
|
||||||
gasAfter := ctx.GasMeter().GasConsumed()
|
gasAfter := ctx.GasMeter().GasConsumed()
|
||||||
require.Equal(t, uint64(0x11542), gasAfter-gasBefore)
|
require.Equal(t, uint64(0x1155d), gasAfter-gasBefore)
|
||||||
|
|
||||||
// ensure it is stored properly
|
// ensure it is stored properly
|
||||||
info := keeper.GetContractInfo(ctx, addr)
|
info := keeper.GetContractInfo(ctx, addr)
|
||||||
@@ -527,7 +527,7 @@ func TestExecute(t *testing.T) {
|
|||||||
|
|
||||||
// make sure gas is properly deducted from ctx
|
// make sure gas is properly deducted from ctx
|
||||||
gasAfter := ctx.GasMeter().GasConsumed()
|
gasAfter := ctx.GasMeter().GasConsumed()
|
||||||
require.Equal(t, uint64(0x11bfb), gasAfter-gasBefore)
|
require.Equal(t, uint64(0x11c16), gasAfter-gasBefore)
|
||||||
|
|
||||||
// ensure bob now exists and got both payments released
|
// ensure bob now exists and got both payments released
|
||||||
bobAcct = accKeeper.GetAccount(ctx, bob)
|
bobAcct = accKeeper.GetAccount(ctx, bob)
|
||||||
|
|||||||
@@ -36,7 +36,14 @@ func FuzzStateModel(m *types.Model, c fuzz.Continue) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func FuzzAccessType(m *types.AccessType, c fuzz.Continue) {
|
func FuzzAccessType(m *types.AccessType, c fuzz.Continue) {
|
||||||
*m = types.AllAccessTypes[c.Int()%len(types.AllAccessTypes)]
|
pos := c.Int() % len(types.AllAccessTypes)
|
||||||
|
for k, _ := range types.AllAccessTypes {
|
||||||
|
if pos == 0 {
|
||||||
|
*m = k
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pos--
|
||||||
|
}
|
||||||
}
|
}
|
||||||
func FuzzAccessConfig(m *types.AccessConfig, c fuzz.Continue) {
|
func FuzzAccessConfig(m *types.AccessConfig, c fuzz.Continue) {
|
||||||
FuzzAccessType(&m.Type, c)
|
FuzzAccessType(&m.Type, c)
|
||||||
|
|||||||
@@ -21,9 +21,9 @@ func (s Sequence) ValidateBasic() error {
|
|||||||
// GenesisState is the struct representation of the export genesis
|
// GenesisState is the struct representation of the export genesis
|
||||||
type GenesisState struct {
|
type GenesisState struct {
|
||||||
Params Params `json:"params"`
|
Params Params `json:"params"`
|
||||||
Codes []Code `json:"codes"`
|
Codes []Code `json:"codes,omitempty"`
|
||||||
Contracts []Contract `json:"contracts"`
|
Contracts []Contract `json:"contracts,omitempty"`
|
||||||
Sequences []Sequence `json:"sequences"`
|
Sequences []Sequence `json:"sequences,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s GenesisState) ValidateBasic() error {
|
func (s GenesisState) ValidateBasic() error {
|
||||||
|
|||||||
@@ -18,16 +18,20 @@ const (
|
|||||||
var ParamStoreKeyUploadAccess = []byte("uploadAccess")
|
var ParamStoreKeyUploadAccess = []byte("uploadAccess")
|
||||||
var ParamStoreKeyInstantiateAccess = []byte("instantiateAccess")
|
var ParamStoreKeyInstantiateAccess = []byte("instantiateAccess")
|
||||||
|
|
||||||
type AccessType uint8
|
type AccessType string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Undefined AccessType = 0
|
Undefined AccessType = "Undefined"
|
||||||
Nobody AccessType = 1
|
Nobody AccessType = "Nobody"
|
||||||
OnlyAddress AccessType = 2
|
OnlyAddress AccessType = "OnlyAddress"
|
||||||
Everybody AccessType = 3
|
Everybody AccessType = "Everybody"
|
||||||
)
|
)
|
||||||
|
|
||||||
var AllAccessTypes = []AccessType{Nobody, OnlyAddress, Everybody}
|
var AllAccessTypes = map[AccessType]struct{}{
|
||||||
|
Nobody: {},
|
||||||
|
OnlyAddress: {},
|
||||||
|
Everybody: {},
|
||||||
|
}
|
||||||
|
|
||||||
func (a AccessType) With(addr sdk.AccAddress) AccessConfig {
|
func (a AccessType) With(addr sdk.AccAddress) AccessConfig {
|
||||||
switch a {
|
switch a {
|
||||||
@@ -44,9 +48,26 @@ func (a AccessType) With(addr sdk.AccAddress) AccessConfig {
|
|||||||
panic("unsupported access type")
|
panic("unsupported access type")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *AccessType) UnmarshalText(text []byte) error {
|
||||||
|
s := AccessType(text)
|
||||||
|
if _, ok := AllAccessTypes[s]; ok {
|
||||||
|
*a = s
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
*a = Undefined
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a AccessType) MarshalText() ([]byte, error) {
|
||||||
|
if _, ok := AllAccessTypes[a]; ok {
|
||||||
|
return []byte(a), nil
|
||||||
|
}
|
||||||
|
return []byte(Undefined), nil
|
||||||
|
}
|
||||||
|
|
||||||
type AccessConfig struct {
|
type AccessConfig struct {
|
||||||
Type AccessType `json:"type" yaml:"type"`
|
Type AccessType `json:"permission" yaml:"permission"`
|
||||||
Address sdk.AccAddress `json:"address" yaml:"address"`
|
Address sdk.AccAddress `json:"address,omitempty" yaml:"address"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a AccessConfig) Equals(o AccessConfig) bool {
|
func (a AccessConfig) Equals(o AccessConfig) bool {
|
||||||
@@ -61,7 +82,7 @@ var (
|
|||||||
|
|
||||||
// Params defines the set of wasm parameters.
|
// Params defines the set of wasm parameters.
|
||||||
type Params struct {
|
type Params struct {
|
||||||
UploadAccess AccessConfig `json:"upload_access" yaml:"upload_access"`
|
UploadAccess AccessConfig `json:"code_upload_access" yaml:"code_upload_access"`
|
||||||
DefaultInstantiatePermission AccessType `json:"instantiate_default_permission" yaml:"instantiate_default_permission"`
|
DefaultInstantiatePermission AccessType `json:"instantiate_default_permission" yaml:"instantiate_default_permission"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,12 +139,10 @@ func validateAccessType(i interface{}) error {
|
|||||||
if v == Undefined {
|
if v == Undefined {
|
||||||
return sdkerrors.Wrap(ErrEmpty, "type")
|
return sdkerrors.Wrap(ErrEmpty, "type")
|
||||||
}
|
}
|
||||||
for i := range AllAccessTypes {
|
if _, ok := AllAccessTypes[v]; !ok {
|
||||||
if AllAccessTypes[i] == v {
|
return sdkerrors.Wrapf(ErrInvalid, "unknown type: %q", v)
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return sdkerrors.Wrapf(ErrInvalid, "unknown type: %d", v)
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v AccessConfig) ValidateBasic() error {
|
func (v AccessConfig) ValidateBasic() error {
|
||||||
@@ -138,7 +157,7 @@ func (v AccessConfig) ValidateBasic() error {
|
|||||||
case OnlyAddress:
|
case OnlyAddress:
|
||||||
return sdk.VerifyAddressFormat(v.Address)
|
return sdk.VerifyAddressFormat(v.Address)
|
||||||
}
|
}
|
||||||
return sdkerrors.Wrapf(ErrInvalid, "unknown type: %d", v.Type)
|
return sdkerrors.Wrapf(ErrInvalid, "unknown type: %q", v.Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v AccessConfig) Allowed(actor sdk.AccAddress) bool {
|
func (v AccessConfig) Allowed(actor sdk.AccAddress) bool {
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
package types
|
package types
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -41,14 +43,14 @@ func TestValidateParams(t *testing.T) {
|
|||||||
"reject empty type in instantiate permission": {
|
"reject empty type in instantiate permission": {
|
||||||
src: Params{
|
src: Params{
|
||||||
UploadAccess: AllowNobody,
|
UploadAccess: AllowNobody,
|
||||||
DefaultInstantiatePermission: 0,
|
DefaultInstantiatePermission: "",
|
||||||
},
|
},
|
||||||
expErr: true,
|
expErr: true,
|
||||||
},
|
},
|
||||||
"reject unknown type in instantiate": {
|
"reject unknown type in instantiate": {
|
||||||
src: Params{
|
src: Params{
|
||||||
UploadAccess: AllowNobody,
|
UploadAccess: AllowNobody,
|
||||||
DefaultInstantiatePermission: 4,
|
DefaultInstantiatePermission: "Undefined",
|
||||||
},
|
},
|
||||||
expErr: true,
|
expErr: true,
|
||||||
},
|
},
|
||||||
@@ -84,5 +86,44 @@ func TestValidateParams(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAccessTypeMarshalJson(t *testing.T) {
|
||||||
|
specs := map[string]struct {
|
||||||
|
src AccessType
|
||||||
|
exp string
|
||||||
|
}{
|
||||||
|
"Undefined": {src: Undefined, exp: `"Undefined"`},
|
||||||
|
"Nobody": {src: Nobody, exp: `"Nobody"`},
|
||||||
|
"OnlyAddress": {src: OnlyAddress, exp: `"OnlyAddress"`},
|
||||||
|
"Everybody": {src: Everybody, exp: `"Everybody"`},
|
||||||
|
"unknown": {src: "", exp: `"Undefined"`},
|
||||||
|
}
|
||||||
|
for msg, spec := range specs {
|
||||||
|
t.Run(msg, func(t *testing.T) {
|
||||||
|
got, err := json.Marshal(spec.src)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, []byte(spec.exp), got)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func TestAccessTypeUnMarshalJson(t *testing.T) {
|
||||||
|
specs := map[string]struct {
|
||||||
|
src string
|
||||||
|
exp AccessType
|
||||||
|
}{
|
||||||
|
"Undefined": {src: `"Undefined"`, exp: Undefined},
|
||||||
|
"Nobody": {src: `"Nobody"`, exp: Nobody},
|
||||||
|
"OnlyAddress": {src: `"OnlyAddress"`, exp: OnlyAddress},
|
||||||
|
"Everybody": {src: `"Everybody"`, exp: Everybody},
|
||||||
|
"unknown": {src: `""`, exp: Undefined},
|
||||||
|
}
|
||||||
|
for msg, spec := range specs {
|
||||||
|
t.Run(msg, func(t *testing.T) {
|
||||||
|
var got AccessType
|
||||||
|
err := json.Unmarshal([]byte(spec.src), &got)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, spec.exp, got)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user