Skip to content

Commit

Permalink
Merge branch 'main' into dwedul/1753-copy-sanction-in
Browse files Browse the repository at this point in the history
# Conflicts:
#	x/README.md
  • Loading branch information
SpicyLemon committed Nov 28, 2023
2 parents bfd2997 + b16a8ba commit 33d1abf
Show file tree
Hide file tree
Showing 14 changed files with 1,285 additions and 28 deletions.
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,14 @@ Ref: https://keepachangelog.com/en/1.0.0/
### Features

* Add CLI commands for the exchange module endpoints and queries [#1701](https://github.com/provenance-io/provenance/issues/1701).
* Add CLI command to generate autocomplete shell scripts [#1762](https://github.com/provenance-io/provenance/pull/1762).
* Create CLI commands for adding a market to a genesis file [#1757](https://github.com/provenance-io/provenance/issues/1757).

### Improvements

* Add upgrade handler for 1.18 [#1756](https://github.com/provenance-io/provenance/pull/1756).
* Updated documentation for each module to work with docusaurus [PR 1763](https://github.com/provenance-io/provenance/pull/1763)
* Updated documentation for each module to work with docusaurus [PR 1763](https://github.com/provenance-io/provenance/pull/1763).
* Create a default market in `make run`, `localnet`, `devnet` and the `provenanced testnet` command [#1757](https://github.com/provenance-io/provenance/issues/1757).

### Dependencies

Expand Down
54 changes: 54 additions & 0 deletions cmd/provenanced/cmd/cmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/cosmos/cosmos-sdk/x/genutil/client/cli"

"github.com/provenance-io/provenance/cmd/provenanced/cmd"
"github.com/provenance-io/provenance/testutil/assertions"
)

func TestInitCmd(t *testing.T) {
Expand All @@ -24,3 +25,56 @@ func TestInitCmd(t *testing.T) {
err := cmd.Execute(rootCmd)
require.NoError(t, err)
}

func TestGenAutoCompleteCmd(t *testing.T) {
home := t.TempDir()

tests := []struct {
name string
args []string
err string
}{
{
name: "failure - missing arg",
err: "accepts 1 arg(s), received 0",
},
{
name: "failure - too many args",
args: []string{"bash", "fish"},
err: "accepts 1 arg(s), received 2",
},
{
name: "failure - invalid shell type",
args: []string{"badshellname"},
err: "shell badshellname is not supported",
},
{
name: "success - works with bash",
args: []string{"bash"},
},
{
name: "success - works with zsh",
args: []string{"zsh"},
},
{
name: "success - works with fish",
args: []string{"fish"},
},
{
name: "success - works with powershell",
args: []string{"powershell"},
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
args := []string{"--home", home, "enable-cli-autocomplete"}
args = append(args, tc.args...)

rootCmd, _ := cmd.NewRootCmd(false)
rootCmd.SetArgs(args)
err := cmd.Execute(rootCmd)
assertions.AssertErrorValue(t, err, tc.err, "should have the correct output value")
})
}
}
13 changes: 13 additions & 0 deletions cmd/provenanced/cmd/export_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package cmd

// This file is in the cmd package (not cmd_test) so that it can expose
// some private keeper stuff for unit testing.

var (
// MakeDefaultMarket is a test-only exposure of makeDefaultMarket.
MakeDefaultMarket = makeDefaultMarket
// AddMarketsToAppState is a test-only exposure of addMarketsToAppState.
AddMarketsToAppState = addMarketsToAppState
// GetNextAvailableMarketID is a test-only exposure of getNextAvailableMarketID.
GetNextAvailableMarketID = getNextAvailableMarketID
)
235 changes: 235 additions & 0 deletions cmd/provenanced/cmd/genaccounts.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ import (
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
"github.com/cosmos/cosmos-sdk/x/genutil"
genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"

"github.com/provenance-io/provenance/x/exchange"
exchangecli "github.com/provenance-io/provenance/x/exchange/client/cli"
markercli "github.com/provenance-io/provenance/x/marker/client/cli"
markertypes "github.com/provenance-io/provenance/x/marker/types"
msgfeetypes "github.com/provenance-io/provenance/x/msgfees/types"
Expand All @@ -40,8 +43,55 @@ const (
flagEscrow = "escrow"
flagActivate = "activate"
flagFinalize = "finalize"

flagDenom = "denom"
)

// appStateUpdater is a function that makes modifications to an app-state.
// Use one in conjunction with updateGenesisFileRunE if your command only needs to parse
// some inputs to make additions or changes to app-state,
type appStateUpdater func(clientCtx client.Context, cmd *cobra.Command, args []string, appState map[string]json.RawMessage) error

// updateGenesisFile reads the existing genesis file, runs the app-state through the
// provided updater, then saves the updated genesis state over the existing genesis file.
func updateGenesisFile(cmd *cobra.Command, args []string, updater appStateUpdater) error {
clientCtx := client.GetClientContextFromCmd(cmd)
serverCtx := server.GetServerContextFromCmd(cmd)
config := serverCtx.Config
config.SetRoot(clientCtx.HomeDir)
genFile := config.GenesisFile()

// Get existing gen state
appState, genDoc, err := genutiltypes.GenesisStateFromGenFile(genFile)
if err != nil {
cmd.SilenceUsage = true
return fmt.Errorf("failed to read genesis file: %w", err)
}

Check warning on line 69 in cmd/provenanced/cmd/genaccounts.go

View check run for this annotation

Codecov / codecov/patch

cmd/provenanced/cmd/genaccounts.go#L67-L69

Added lines #L67 - L69 were not covered by tests

err = updater(clientCtx, cmd, args, appState)
if err != nil {
return err
}

// None of the possible errors that might come after this will be helped by printing usage with them.
cmd.SilenceUsage = true

appStateJSON, err := json.Marshal(appState)
if err != nil {
return fmt.Errorf("failed to marshal application genesis state: %w", err)
}

Check warning on line 82 in cmd/provenanced/cmd/genaccounts.go

View check run for this annotation

Codecov / codecov/patch

cmd/provenanced/cmd/genaccounts.go#L81-L82

Added lines #L81 - L82 were not covered by tests

genDoc.AppState = appStateJSON
return genutil.ExportGenesisFile(genDoc, genFile)
}

// updateGenesisFileRunE returns a cobra.Command.RunE function that runs updateGenesisFile using the provided updater.
func updateGenesisFileRunE(updater appStateUpdater) func(*cobra.Command, []string) error {
return func(cmd *cobra.Command, args []string) error {
return updateGenesisFile(cmd, args, updater)
}
}

// AddGenesisAccountCmd returns add-genesis-account cobra Command.
func AddGenesisAccountCmd(defaultNodeHome string) *cobra.Command {
cmd := &cobra.Command{
Expand Down Expand Up @@ -640,3 +690,188 @@ func checkMsgTypeValid(registry types.InterfaceRegistry, msgTypeURL string) erro
}
return err
}

// AddGenesisDefaultMarketCmd returns add-genesis-default-market cobra command.
func AddGenesisDefaultMarketCmd(defaultNodeHome string) *cobra.Command {
cmd := &cobra.Command{
Use: `add-genesis-default-market [--denom <denom>]
All base accounts already in the genesis file will be given all permissions on this market.
An error is returned if no such accounts are in the existing genesis file.
If --denom <denom> is not provided, the staking bond denom is used.
If no staking bond denom is defined either, then nhash is used.
This command is equivalent to the following command:
$ ` + version.AppName + ` add-genesis-custom-market \
--name 'Default <denom> Market' \
--create-ask 100<denom> --create-bid 100<denom> \
--seller-flat 500<denom> --buyer-flat 500<denom> \
--seller-ratios 20<denom>:1<denom> --buyer-ratios 20<denom>:1<denom> \
--accepting-orders --allow-user-settle \
--access-grants <all permissions to all known base accounts>
`,
Short: "Add a default market to the genesis file",
DisableFlagsInUseLine: true,
Args: cobra.NoArgs,
RunE: updateGenesisFileRunE(func(clientCtx client.Context, cmd *cobra.Command, _ []string, appState map[string]json.RawMessage) error {
// Printing usage with the errors from here won't actually help anyone.
cmd.SilenceUsage = true

// Identify the accounts that will get all the permissions.
var authGen authtypes.GenesisState
err := clientCtx.Codec.UnmarshalJSON(appState[authtypes.ModuleName], &authGen)
if err != nil {
return fmt.Errorf("could not extract auth genesis state: %w", err)
}

Check warning on line 727 in cmd/provenanced/cmd/genaccounts.go

View check run for this annotation

Codecov / codecov/patch

cmd/provenanced/cmd/genaccounts.go#L726-L727

Added lines #L726 - L727 were not covered by tests
genAccts, err := authtypes.UnpackAccounts(authGen.Accounts)
if err != nil {
return fmt.Errorf("could not unpack genesis acounts: %w", err)
}

Check warning on line 731 in cmd/provenanced/cmd/genaccounts.go

View check run for this annotation

Codecov / codecov/patch

cmd/provenanced/cmd/genaccounts.go#L730-L731

Added lines #L730 - L731 were not covered by tests
addrs := make([]string, 0, len(genAccts))
for _, acct := range genAccts {
// We specifically only want accounts that are base accounts (i.e. no markers or others).
// This should also include the validator accounts (that have already been added).
baseAcct, ok := acct.(*authtypes.BaseAccount)
if ok {
addrs = append(addrs, baseAcct.Address)
}
}
if len(addrs) == 0 {
return errors.New("genesis file must have one or more BaseAccount before a default market can be added")
}

Check warning on line 743 in cmd/provenanced/cmd/genaccounts.go

View check run for this annotation

Codecov / codecov/patch

cmd/provenanced/cmd/genaccounts.go#L742-L743

Added lines #L742 - L743 were not covered by tests

// Identify the denom that we'll use for the fees.
feeDenom, err := cmd.Flags().GetString(flagDenom)
if err != nil {
return fmt.Errorf("error reading --%s value: %w", flagDenom, err)
}

Check warning on line 749 in cmd/provenanced/cmd/genaccounts.go

View check run for this annotation

Codecov / codecov/patch

cmd/provenanced/cmd/genaccounts.go#L748-L749

Added lines #L748 - L749 were not covered by tests
if len(feeDenom) == 0 {
var stGen stakingtypes.GenesisState
err = clientCtx.Codec.UnmarshalJSON(appState[stakingtypes.ModuleName], &stGen)
if err == nil {
feeDenom = stGen.Params.BondDenom
}

Check warning on line 755 in cmd/provenanced/cmd/genaccounts.go

View check run for this annotation

Codecov / codecov/patch

cmd/provenanced/cmd/genaccounts.go#L754-L755

Added lines #L754 - L755 were not covered by tests
}
if len(feeDenom) == 0 {
feeDenom = "nhash"
}
if err = sdk.ValidateDenom(feeDenom); err != nil {
return err
}

Check warning on line 762 in cmd/provenanced/cmd/genaccounts.go

View check run for this annotation

Codecov / codecov/patch

cmd/provenanced/cmd/genaccounts.go#L761-L762

Added lines #L761 - L762 were not covered by tests

// Create the market and add it to the app state.
market := makeDefaultMarket(feeDenom, addrs)
return addMarketsToAppState(clientCtx, appState, market)
}),
}

cmd.Flags().String(flags.FlagHome, defaultNodeHome, "The application home directory")
cmd.Flags().String(flagDenom, "", "The fee denom for the market")
return cmd
}

// makeDefaultMarket creates the default market that uses the provided fee denom
// and gives all permissions to each of the provided addrs.
func makeDefaultMarket(feeDenom string, addrs []string) exchange.Market {
market := exchange.Market{
MarketDetails: exchange.MarketDetails{Name: "Default Market"},
AcceptingOrders: true,
AllowUserSettlement: true,
}

if len(feeDenom) > 0 {
creationFee := sdk.NewCoins(sdk.NewInt64Coin(feeDenom, 100))
settlementFlat := sdk.NewCoins(sdk.NewInt64Coin(feeDenom, 500))
settlementRatio := []exchange.FeeRatio{{Price: sdk.NewInt64Coin(feeDenom, 20), Fee: sdk.NewInt64Coin(feeDenom, 1)}}
market.MarketDetails.Name = fmt.Sprintf("Default %s Market", feeDenom)
market.FeeCreateAskFlat = creationFee
market.FeeCreateBidFlat = creationFee
market.FeeSellerSettlementFlat = settlementFlat
market.FeeSellerSettlementRatios = settlementRatio
market.FeeBuyerSettlementFlat = settlementFlat
market.FeeBuyerSettlementRatios = settlementRatio
}

for _, addr := range addrs {
market.AccessGrants = append(market.AccessGrants,
exchange.AccessGrant{Address: addr, Permissions: exchange.AllPermissions()})
}
return market
}

// AddGenesisCustomMarketCmd returns add-genesis-custom-market cobra command.
func AddGenesisCustomMarketCmd(defaultNodeHome string) *cobra.Command {
cmd := &cobra.Command{
Use: "add-genesis-custom-market",
Short: "Add a market to the genesis file",
RunE: updateGenesisFileRunE(func(clientCtx client.Context, cmd *cobra.Command, args []string, appState map[string]json.RawMessage) error {
msg, err := exchangecli.MakeMsgGovCreateMarket(clientCtx, cmd.Flags(), args)
if err != nil {
return err
}

// Now that we've read all the flags and stuff, no need to show usage with any errors anymore in here.
cmd.SilenceUsage = true
return addMarketsToAppState(clientCtx, appState, msg.Market)
}),
}

cmd.Flags().String(flags.FlagHome, defaultNodeHome, "The application home directory")
exchangecli.SetupCmdTxGovCreateMarket(cmd)
exchangecli.AddUseDetails(cmd, "If no <market id> is provided, the next available one will be used.")
return cmd
}

// addMarketsToAppState adds the given markets to the app state in the exchange module.
// If a provided market's MarketId is 0, the next available one will be identified and used.
func addMarketsToAppState(clientCtx client.Context, appState map[string]json.RawMessage, markets ...exchange.Market) error {
cdc := clientCtx.Codec
var exGenState exchange.GenesisState
if len(appState[exchange.ModuleName]) > 0 {
if err := cdc.UnmarshalJSON(appState[exchange.ModuleName], &exGenState); err != nil {
return fmt.Errorf("could not extract exchange genesis state: %w", err)
}
}

for _, market := range markets {
if err := market.Validate(); err != nil {
return err
}

Check warning on line 841 in cmd/provenanced/cmd/genaccounts.go

View check run for this annotation

Codecov / codecov/patch

cmd/provenanced/cmd/genaccounts.go#L840-L841

Added lines #L840 - L841 were not covered by tests

if market.MarketId == 0 {
market.MarketId = getNextAvailableMarketID(exGenState)
if exGenState.LastMarketId < market.MarketId {
exGenState.LastMarketId = market.MarketId
}
}

exGenState.Markets = append(exGenState.Markets, market)
}

exGenStateBz, err := cdc.MarshalJSON(&exGenState)
if err != nil {
return fmt.Errorf("failed to marshal exchange genesis state: %w", err)
}
appState[exchange.ModuleName] = exGenStateBz
return nil
}

// getNextAvailableMarketID returns the next available market id given all the markets in the provided genesis state.
func getNextAvailableMarketID(exGenState exchange.GenesisState) uint32 {
if len(exGenState.Markets) == 0 {
return 1
}

marketIDsMap := make(map[uint32]bool, len(exGenState.Markets))
for _, market := range exGenState.Markets {
marketIDsMap[market.MarketId] = true
}

rv := uint32(1)
for marketIDsMap[rv] {
rv++
}
return rv
}
Loading

0 comments on commit 33d1abf

Please sign in to comment.