From 73195e8e7174ffee28b68935f35f3e0cfb3fcd9f Mon Sep 17 00:00:00 2001 From: Pino' Surace Date: Thu, 14 Sep 2023 21:17:03 +0200 Subject: [PATCH 1/5] Add grant system tests --- tests/system/fraud_test.go | 1 + tests/system/genesis_io.go | 12 ++++++++ tests/system/grant_test.go | 63 ++++++++++++++++++++++++++++++++++++++ tests/system/system.go | 1 - x/wasm/client/cli/tx.go | 14 +++------ 5 files changed, 81 insertions(+), 10 deletions(-) create mode 100644 tests/system/grant_test.go diff --git a/tests/system/fraud_test.go b/tests/system/fraud_test.go index 3fcde936a8..9e5e4d61d5 100644 --- a/tests/system/fraud_test.go +++ b/tests/system/fraud_test.go @@ -13,6 +13,7 @@ import ( ) func TestRecursiveMsgsExternalTrigger(t *testing.T) { + sut.ResetChain(t) const maxBlockGas = 2_000_000 sut.ModifyGenesisJSON(t, SetConsensusMaxGas(t, maxBlockGas)) sut.StartChain(t) diff --git a/tests/system/genesis_io.go b/tests/system/genesis_io.go index 841c18c924..b39024890e 100644 --- a/tests/system/genesis_io.go +++ b/tests/system/genesis_io.go @@ -31,3 +31,15 @@ func GetGenesisBalance(rawGenesis []byte, addr string) sdk.Coins { } return r } + +// SetCodeUploadPermission sets the code upload permissions +func SetCodeUploadPermission(t *testing.T, permission string, addresses []string) GenesisMutator { + return func(genesis []byte) []byte { + t.Helper() + state, err := sjson.Set(string(genesis), "app_state.wasm.params.code_upload_access.permission", permission) + require.NoError(t, err) + state, err = sjson.Set(state, "app_state.wasm.params.code_upload_access.addresses", addresses) + require.NoError(t, err) + return []byte(state) + } +} diff --git a/tests/system/grant_test.go b/tests/system/grant_test.go new file mode 100644 index 0000000000..8e7588b076 --- /dev/null +++ b/tests/system/grant_test.go @@ -0,0 +1,63 @@ +//go:build system_test + +package system + +import ( + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/tidwall/gjson" +) + +func TestGrantStoreCode(t *testing.T) { + sut.ResetChain(t) + cli := NewWasmdCLI(t, sut, verbose) + + // add genesis account with some tokens + account1Addr := cli.AddKey("account1") + + // add genesis account with some tokens + account2Addr := cli.AddKey("account2") + + sut.ModifyGenesisCLI(t, + []string{"genesis", "add-genesis-account", account1Addr, "100000000stake"}, + ) + sut.ModifyGenesisCLI(t, + []string{"genesis", "add-genesis-account", account2Addr, "100000000stake"}, + ) + + //set params + sut.ModifyGenesisJSON(t, SetCodeUploadPermission(t, "AnyOfAddresses", []string{account1Addr})) + sut.StartChain(t) + + // query validator address to delegate tokens + rsp := cli.CustomQuery("q", "wasm", "params") + permission := gjson.Get(rsp, "code_upload_access.permission").String() + addresses := gjson.Get(rsp, "code_upload_access.addresses").Array() + + assert.Equal(t, permission, "AnyOfAddresses") + assert.Equal(t, []string{account1Addr}, addresses) + + // grant upload permission to address2 + rsp = cli.CustomCommand("tx", "wasm", "grant-store-code", account2Addr, "*:*", "--from="+account1Addr) + RequireTxSuccess(t, rsp) + + // address2 store code fails + rsp = cli.CustomCommand("tx", "wasm", "store", "./testdata/hackatom.wasm.gzip", "--from="+account2Addr, "--gas=1500000", "--fees=2stake") + RequireTxFailure(t, rsp) + + args := cli.withTXFlags("tx", "wasm", "store", "./testdata/hackatom.wasm.gzip", "--from="+account2Addr, "--generate-only") + tx, ok := cli.run(args) + require.True(t, ok) + + pathToTx := filepath.Join(t.TempDir(), "tx.json") + err := os.WriteFile(pathToTx, []byte(tx), os.FileMode(0o744)) + require.NoError(t, err) + + // address2 authz exec store code should succeed + rsp = cli.CustomCommand("tx", "authz", "exec", pathToTx, "--from="+account2Addr, "--gas=1500000", "--fees=2stake") + RequireTxSuccess(t, rsp) +} diff --git a/tests/system/system.go b/tests/system/system.go index 89b5ca0543..47cbdf1425 100644 --- a/tests/system/system.go +++ b/tests/system/system.go @@ -390,7 +390,6 @@ type GenesisMutator func([]byte) []byte // return state // } func (s *SystemUnderTest) ModifyGenesisJSON(t *testing.T, mutators ...GenesisMutator) { - s.ResetChain(t) s.modifyGenesisJSON(t, mutators...) } diff --git a/x/wasm/client/cli/tx.go b/x/wasm/client/cli/tx.go index 2fa1b94165..d5df24239f 100644 --- a/x/wasm/client/cli/tx.go +++ b/x/wasm/client/cli/tx.go @@ -560,15 +560,15 @@ $ %s tx grant execution --allow-all-messages --ma func GrantStoreCodeAuthorizationCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "grant [grantee] store-code [code_hash:permission]", + Use: "grant-store-code [grantee] [code_hash:permission]", Short: "Grant authorization to an address", Long: fmt.Sprintf(`Grant authorization to an address. Examples: -$ %s tx grant store-code 13a1fc994cc6d1c81b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2a5:everybody 1wqrtry681b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2a5:nobody --expiration 1667979596 +$ %s tx grant-store-code 13a1fc994cc6d1c81b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2a5:everybody 1wqrtry681b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2a5:nobody --expiration 1667979596 -$ %s tx grant store-code *:%s1l2rsakp388kuv9k8qzq6lrm9taddae7fpx59wm,%s1vx8knpllrj7n963p9ttd80w47kpacrhuts497x +$ %s tx grant-store-code *:%s1l2rsakp388kuv9k8qzq6lrm9taddae7fpx59wm,%s1vx8knpllrj7n963p9ttd80w47kpacrhuts497x `, version.AppName, version.AppName, version.AppName, version.AppName), - Args: cobra.MinimumNArgs(3), + Args: cobra.MinimumNArgs(2), RunE: func(cmd *cobra.Command, args []string) error { clientCtx, err := client.GetClientTxContext(cmd) if err != nil { @@ -580,11 +580,7 @@ $ %s tx grant store-code *:%s1l2rsakp388kuv9k8qzq6lrm9taddae7fpx5 return err } - if args[1] != "store-code" { - return fmt.Errorf("%s authorization type not supported", args[1]) - } - - grants, err := parseStoreCodeGrants(args[2:]) + grants, err := parseStoreCodeGrants(args[1:]) if err != nil { return err } From 4f5b0060a1e7581bb2397e9a0d82ac6fa22d79b2 Mon Sep 17 00:00:00 2001 From: Pino' Surace Date: Fri, 15 Sep 2023 08:18:39 +0200 Subject: [PATCH 2/5] Add unpermissioned chain test case --- tests/system/grant_test.go | 58 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 3 deletions(-) diff --git a/tests/system/grant_test.go b/tests/system/grant_test.go index 8e7588b076..dd185b5ba4 100644 --- a/tests/system/grant_test.go +++ b/tests/system/grant_test.go @@ -12,7 +12,9 @@ import ( "github.com/tidwall/gjson" ) -func TestGrantStoreCode(t *testing.T) { +func TestGrantStoreCodePermissioned(t *testing.T) { + t.Skip() + sut.ResetChain(t) cli := NewWasmdCLI(t, sut, verbose) @@ -33,7 +35,7 @@ func TestGrantStoreCode(t *testing.T) { sut.ModifyGenesisJSON(t, SetCodeUploadPermission(t, "AnyOfAddresses", []string{account1Addr})) sut.StartChain(t) - // query validator address to delegate tokens + // query params rsp := cli.CustomQuery("q", "wasm", "params") permission := gjson.Get(rsp, "code_upload_access.permission").String() addresses := gjson.Get(rsp, "code_upload_access.addresses").Array() @@ -41,7 +43,7 @@ func TestGrantStoreCode(t *testing.T) { assert.Equal(t, permission, "AnyOfAddresses") assert.Equal(t, []string{account1Addr}, addresses) - // grant upload permission to address2 + // address1 grant upload permission to address2 rsp = cli.CustomCommand("tx", "wasm", "grant-store-code", account2Addr, "*:*", "--from="+account1Addr) RequireTxSuccess(t, rsp) @@ -61,3 +63,53 @@ func TestGrantStoreCode(t *testing.T) { rsp = cli.CustomCommand("tx", "authz", "exec", pathToTx, "--from="+account2Addr, "--gas=1500000", "--fees=2stake") RequireTxSuccess(t, rsp) } + +func TestGrantStoreCode(t *testing.T) { + sut.ResetChain(t) + cli := NewWasmdCLI(t, sut, verbose) + + // add genesis account with some tokens + account1Addr := cli.AddKey("account1") + + // add genesis account with some tokens + account2Addr := cli.AddKey("account2") + + sut.ModifyGenesisCLI(t, + []string{"genesis", "add-genesis-account", account1Addr, "100000000stake"}, + ) + sut.ModifyGenesisCLI(t, + []string{"genesis", "add-genesis-account", account2Addr, "100000000stake"}, + ) + + sut.StartChain(t) + + // address1 grant upload permission to address2 + rsp := cli.CustomCommand("tx", "wasm", "grant-store-code", account2Addr, "*:nobody", "--from="+account1Addr) + RequireTxSuccess(t, rsp) + + // create tx - permission everybody + args := cli.withTXFlags("tx", "wasm", "store", "./testdata/hackatom.wasm.gzip", "--instantiate-everybody=true", "--from="+account2Addr, "--generate-only") + tx, ok := cli.run(args) + require.True(t, ok) + + pathToTx := filepath.Join(t.TempDir(), "tx.json") + err := os.WriteFile(pathToTx, []byte(tx), os.FileMode(0o744)) + require.NoError(t, err) + + // address2 authz exec fails because instantiate permissions do not match + rsp = cli.CustomCommand("tx", "authz", "exec", pathToTx, "--from="+account2Addr, "--gas=1500000", "--fees=2stake") + RequireTxSuccess(t, rsp) + + // create tx - permission nobody + args = cli.withTXFlags("tx", "wasm", "store", "./testdata/hackatom.wasm.gzip", "--instantiate-nobody=true", "--from="+account2Addr, "--generate-only") + tx, ok = cli.run(args) + require.True(t, ok) + + pathToTx = filepath.Join(t.TempDir(), "tx.json") + err = os.WriteFile(pathToTx, []byte(tx), os.FileMode(0o744)) + require.NoError(t, err) + + // address2 authz exec succeeds + rsp = cli.CustomCommand("tx", "authz", "exec", pathToTx, "--from="+account2Addr, "--gas=1500000", "--fees=2stake") + RequireTxSuccess(t, rsp) +} From da9831844c4005434eefee2e991cf28657a3ad05 Mon Sep 17 00:00:00 2001 From: Pino' Surace Date: Fri, 15 Sep 2023 08:44:19 +0200 Subject: [PATCH 3/5] Fix tests --- tests/system/grant_test.go | 18 +++++++++--------- x/wasm/types/authz.go | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/system/grant_test.go b/tests/system/grant_test.go index dd185b5ba4..1262dc7761 100644 --- a/tests/system/grant_test.go +++ b/tests/system/grant_test.go @@ -12,9 +12,7 @@ import ( "github.com/tidwall/gjson" ) -func TestGrantStoreCodePermissioned(t *testing.T) { - t.Skip() - +func TestGrantStoreCodePermissionedChain(t *testing.T) { sut.ResetChain(t) cli := NewWasmdCLI(t, sut, verbose) @@ -38,10 +36,11 @@ func TestGrantStoreCodePermissioned(t *testing.T) { // query params rsp := cli.CustomQuery("q", "wasm", "params") permission := gjson.Get(rsp, "code_upload_access.permission").String() - addresses := gjson.Get(rsp, "code_upload_access.addresses").Array() + addrRes := gjson.Get(rsp, "code_upload_access.addresses").Array() + assert.Equal(t, 1, len(addrRes)) assert.Equal(t, permission, "AnyOfAddresses") - assert.Equal(t, []string{account1Addr}, addresses) + assert.Equal(t, account1Addr, addrRes[0].Str) // address1 grant upload permission to address2 rsp = cli.CustomCommand("tx", "wasm", "grant-store-code", account2Addr, "*:*", "--from="+account1Addr) @@ -51,7 +50,8 @@ func TestGrantStoreCodePermissioned(t *testing.T) { rsp = cli.CustomCommand("tx", "wasm", "store", "./testdata/hackatom.wasm.gzip", "--from="+account2Addr, "--gas=1500000", "--fees=2stake") RequireTxFailure(t, rsp) - args := cli.withTXFlags("tx", "wasm", "store", "./testdata/hackatom.wasm.gzip", "--from="+account2Addr, "--generate-only") + // create tx + args := cli.withTXFlags("tx", "wasm", "store", "./testdata/hackatom.wasm.gzip", "--from="+account1Addr, "--generate-only") tx, ok := cli.run(args) require.True(t, ok) @@ -88,7 +88,7 @@ func TestGrantStoreCode(t *testing.T) { RequireTxSuccess(t, rsp) // create tx - permission everybody - args := cli.withTXFlags("tx", "wasm", "store", "./testdata/hackatom.wasm.gzip", "--instantiate-everybody=true", "--from="+account2Addr, "--generate-only") + args := cli.withTXFlags("tx", "wasm", "store", "./testdata/hackatom.wasm.gzip", "--instantiate-everybody=true", "--from="+account1Addr, "--generate-only") tx, ok := cli.run(args) require.True(t, ok) @@ -98,10 +98,10 @@ func TestGrantStoreCode(t *testing.T) { // address2 authz exec fails because instantiate permissions do not match rsp = cli.CustomCommand("tx", "authz", "exec", pathToTx, "--from="+account2Addr, "--gas=1500000", "--fees=2stake") - RequireTxSuccess(t, rsp) + RequireTxFailure(t, rsp) // create tx - permission nobody - args = cli.withTXFlags("tx", "wasm", "store", "./testdata/hackatom.wasm.gzip", "--instantiate-nobody=true", "--from="+account2Addr, "--generate-only") + args = cli.withTXFlags("tx", "wasm", "store", "./testdata/hackatom.wasm.gzip", "--instantiate-nobody=true", "--from="+account1Addr, "--generate-only") tx, ok = cli.run(args) require.True(t, ok) diff --git a/x/wasm/types/authz.go b/x/wasm/types/authz.go index 7f49a8aea9..7a55dda860 100644 --- a/x/wasm/types/authz.go +++ b/x/wasm/types/authz.go @@ -51,7 +51,7 @@ func (a *StoreCodeAuthorization) Accept(ctx sdk.Context, msg sdk.Msg) (authztype } code := storeMsg.WASMByteCode - permission := *storeMsg.InstantiatePermission + permission := storeMsg.InstantiatePermission if ioutils.IsGzip(code) { gasRegister, ok := GasRegisterFromContext(ctx) @@ -127,7 +127,7 @@ func (g CodeGrant) ValidateBasic() error { } // Accept checks if checksum and permission match the grant -func (g CodeGrant) Accept(checksum []byte, permission AccessConfig) bool { +func (g CodeGrant) Accept(checksum []byte, permission *AccessConfig) bool { if !strings.EqualFold(string(g.CodeHash), CodehashWildcard) && !bytes.EqualFold(g.CodeHash, checksum) { return false } From e15f0522de480c502e7bfe2c5302d59aa0980681 Mon Sep 17 00:00:00 2001 From: Pino' Surace Date: Fri, 15 Sep 2023 10:53:53 +0200 Subject: [PATCH 4/5] Update cli and fix feedbacks --- tests/system/genesis_io.go | 2 +- tests/system/grant_test.go | 115 --------------- tests/system/permissioned_test.go | 62 ++++++++ x/wasm/client/cli/tx.go | 228 ++++++++++++++---------------- 4 files changed, 167 insertions(+), 240 deletions(-) delete mode 100644 tests/system/grant_test.go create mode 100644 tests/system/permissioned_test.go diff --git a/tests/system/genesis_io.go b/tests/system/genesis_io.go index b39024890e..23e54f09e8 100644 --- a/tests/system/genesis_io.go +++ b/tests/system/genesis_io.go @@ -33,7 +33,7 @@ func GetGenesisBalance(rawGenesis []byte, addr string) sdk.Coins { } // SetCodeUploadPermission sets the code upload permissions -func SetCodeUploadPermission(t *testing.T, permission string, addresses []string) GenesisMutator { +func SetCodeUploadPermission(t *testing.T, permission string, addresses ...string) GenesisMutator { return func(genesis []byte) []byte { t.Helper() state, err := sjson.Set(string(genesis), "app_state.wasm.params.code_upload_access.permission", permission) diff --git a/tests/system/grant_test.go b/tests/system/grant_test.go deleted file mode 100644 index 1262dc7761..0000000000 --- a/tests/system/grant_test.go +++ /dev/null @@ -1,115 +0,0 @@ -//go:build system_test - -package system - -import ( - "os" - "path/filepath" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/tidwall/gjson" -) - -func TestGrantStoreCodePermissionedChain(t *testing.T) { - sut.ResetChain(t) - cli := NewWasmdCLI(t, sut, verbose) - - // add genesis account with some tokens - account1Addr := cli.AddKey("account1") - - // add genesis account with some tokens - account2Addr := cli.AddKey("account2") - - sut.ModifyGenesisCLI(t, - []string{"genesis", "add-genesis-account", account1Addr, "100000000stake"}, - ) - sut.ModifyGenesisCLI(t, - []string{"genesis", "add-genesis-account", account2Addr, "100000000stake"}, - ) - - //set params - sut.ModifyGenesisJSON(t, SetCodeUploadPermission(t, "AnyOfAddresses", []string{account1Addr})) - sut.StartChain(t) - - // query params - rsp := cli.CustomQuery("q", "wasm", "params") - permission := gjson.Get(rsp, "code_upload_access.permission").String() - addrRes := gjson.Get(rsp, "code_upload_access.addresses").Array() - assert.Equal(t, 1, len(addrRes)) - - assert.Equal(t, permission, "AnyOfAddresses") - assert.Equal(t, account1Addr, addrRes[0].Str) - - // address1 grant upload permission to address2 - rsp = cli.CustomCommand("tx", "wasm", "grant-store-code", account2Addr, "*:*", "--from="+account1Addr) - RequireTxSuccess(t, rsp) - - // address2 store code fails - rsp = cli.CustomCommand("tx", "wasm", "store", "./testdata/hackatom.wasm.gzip", "--from="+account2Addr, "--gas=1500000", "--fees=2stake") - RequireTxFailure(t, rsp) - - // create tx - args := cli.withTXFlags("tx", "wasm", "store", "./testdata/hackatom.wasm.gzip", "--from="+account1Addr, "--generate-only") - tx, ok := cli.run(args) - require.True(t, ok) - - pathToTx := filepath.Join(t.TempDir(), "tx.json") - err := os.WriteFile(pathToTx, []byte(tx), os.FileMode(0o744)) - require.NoError(t, err) - - // address2 authz exec store code should succeed - rsp = cli.CustomCommand("tx", "authz", "exec", pathToTx, "--from="+account2Addr, "--gas=1500000", "--fees=2stake") - RequireTxSuccess(t, rsp) -} - -func TestGrantStoreCode(t *testing.T) { - sut.ResetChain(t) - cli := NewWasmdCLI(t, sut, verbose) - - // add genesis account with some tokens - account1Addr := cli.AddKey("account1") - - // add genesis account with some tokens - account2Addr := cli.AddKey("account2") - - sut.ModifyGenesisCLI(t, - []string{"genesis", "add-genesis-account", account1Addr, "100000000stake"}, - ) - sut.ModifyGenesisCLI(t, - []string{"genesis", "add-genesis-account", account2Addr, "100000000stake"}, - ) - - sut.StartChain(t) - - // address1 grant upload permission to address2 - rsp := cli.CustomCommand("tx", "wasm", "grant-store-code", account2Addr, "*:nobody", "--from="+account1Addr) - RequireTxSuccess(t, rsp) - - // create tx - permission everybody - args := cli.withTXFlags("tx", "wasm", "store", "./testdata/hackatom.wasm.gzip", "--instantiate-everybody=true", "--from="+account1Addr, "--generate-only") - tx, ok := cli.run(args) - require.True(t, ok) - - pathToTx := filepath.Join(t.TempDir(), "tx.json") - err := os.WriteFile(pathToTx, []byte(tx), os.FileMode(0o744)) - require.NoError(t, err) - - // address2 authz exec fails because instantiate permissions do not match - rsp = cli.CustomCommand("tx", "authz", "exec", pathToTx, "--from="+account2Addr, "--gas=1500000", "--fees=2stake") - RequireTxFailure(t, rsp) - - // create tx - permission nobody - args = cli.withTXFlags("tx", "wasm", "store", "./testdata/hackatom.wasm.gzip", "--instantiate-nobody=true", "--from="+account1Addr, "--generate-only") - tx, ok = cli.run(args) - require.True(t, ok) - - pathToTx = filepath.Join(t.TempDir(), "tx.json") - err = os.WriteFile(pathToTx, []byte(tx), os.FileMode(0o744)) - require.NoError(t, err) - - // address2 authz exec succeeds - rsp = cli.CustomCommand("tx", "authz", "exec", pathToTx, "--from="+account2Addr, "--gas=1500000", "--fees=2stake") - RequireTxSuccess(t, rsp) -} diff --git a/tests/system/permissioned_test.go b/tests/system/permissioned_test.go new file mode 100644 index 0000000000..92470a9f9b --- /dev/null +++ b/tests/system/permissioned_test.go @@ -0,0 +1,62 @@ +//go:build system_test + +package system + +import ( + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/require" + "github.com/tidwall/gjson" +) + +func TestGrantStoreCodePermissionedChain(t *testing.T) { + sut.ResetChain(t) + cli := NewWasmdCLI(t, sut, verbose) + + chainAuthorizedAccount := cli.AddKey("chain_authorized_account") + devAccount := cli.AddKey("dev_account") + + //set params + sut.ModifyGenesisJSON(t, SetCodeUploadPermission(t, "AnyOfAddresses", chainAuthorizedAccount)) + + sut.ModifyGenesisCLI(t, + []string{"genesis", "add-genesis-account", chainAuthorizedAccount, "100000000stake"}, + ) + sut.ModifyGenesisCLI(t, + []string{"genesis", "add-genesis-account", devAccount, "100000000stake"}, + ) + + sut.StartChain(t) + + // query params + rsp := cli.CustomQuery("q", "wasm", "params") + permission := gjson.Get(rsp, "code_upload_access.permission").String() + addrRes := gjson.Get(rsp, "code_upload_access.addresses").Array() + require.Equal(t, 1, len(addrRes)) + + require.Equal(t, permission, "AnyOfAddresses") + require.Equal(t, chainAuthorizedAccount, addrRes[0].Str) + + // chain_authorized_account grant upload permission to dev_account + rsp = cli.CustomCommand("tx", "wasm", "grant", devAccount, "store-code", "*:*", "--from="+chainAuthorizedAccount) + RequireTxSuccess(t, rsp) + + // dev_account store code fails as the address is not in the code-upload accept-list + rsp = cli.CustomCommand("tx", "wasm", "store", "./testdata/hackatom.wasm.gzip", "--from="+devAccount, "--gas=1500000", "--fees=2stake") + RequireTxFailure(t, rsp) + + // create tx should work for addresses in the accept-list + args := cli.withTXFlags("tx", "wasm", "store", "./testdata/hackatom.wasm.gzip", "--from="+chainAuthorizedAccount, "--generate-only") + tx, ok := cli.run(args) + require.True(t, ok) + + pathToTx := filepath.Join(t.TempDir(), "tx.json") + err := os.WriteFile(pathToTx, []byte(tx), os.FileMode(0o744)) + require.NoError(t, err) + + // store code via authz execution uses the given grant and should succeed + rsp = cli.CustomCommand("tx", "authz", "exec", pathToTx, "--from="+devAccount, "--gas=1500000", "--fees=2stake") + RequireTxSuccess(t, rsp) +} diff --git a/x/wasm/client/cli/tx.go b/x/wasm/client/cli/tx.go index d5df24239f..c2e134751e 100644 --- a/x/wasm/client/cli/tx.go +++ b/x/wasm/client/cli/tx.go @@ -68,7 +68,6 @@ func GetTxCmd() *cobra.Command { UpdateContractAdminCmd(), ClearContractAdminCmd(), GrantAuthorizationCmd(), - GrantStoreCodeAuthorizationCmd(), UpdateInstantiateConfigCmd(), SubmitProposalCmd(), ) @@ -417,7 +416,7 @@ func parseExecuteArgs(contractAddr, execMsg string, sender sdk.AccAddress, flags func GrantAuthorizationCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "grant [grantee] [message_type=\"execution\"|\"migration\"] [contract_addr_bech32] --allow-raw-msgs [msg1,msg2,...] --allow-msg-keys [key1,key2,...] --allow-all-messages", + Use: "grant [grantee] [message_type=\"execution\"|\"migration\"|\"store-code\"]", Short: "Grant authorization to an address", Long: fmt.Sprintf(`Grant authorization to an address. Examples: @@ -426,7 +425,11 @@ $ %s tx grant execution --allow-all-messages --ma $ %s tx grant execution --allow-all-messages --max-funds 100000uwasm --expiration 1667979596 $ %s tx grant execution --allow-all-messages --max-calls 5 --max-funds 100000uwasm --expiration 1667979596 -`, version.AppName, version.AppName, version.AppName), + +$ %s tx grant store-code 13a1fc994cc6d1c81b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2a5:everybody 1wqrtry681b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2a5:nobody --expiration 1667979596 + +$ %s tx grant store-code *:%s1l2rsakp388kuv9k8qzq6lrm9taddae7fpx59wm,%s1vx8knpllrj7n963p9ttd80w47kpacrhuts497x +`, version.AppName, version.AppName, version.AppName, version.AppName, version.AppName, version.AppName, version.AppName), Args: cobra.ExactArgs(3), RunE: func(cmd *cobra.Command, args []string) error { clientCtx, err := client.GetClientTxContext(cmd) @@ -439,98 +442,42 @@ $ %s tx grant execution --allow-all-messages --ma return err } - contract, err := sdk.AccAddressFromBech32(args[2]) - if err != nil { - return err - } - - msgKeys, err := cmd.Flags().GetStringSlice(flagAllowedMsgKeys) - if err != nil { - return err - } - - rawMsgs, err := cmd.Flags().GetStringSlice(flagAllowedRawMsgs) - if err != nil { - return err - } - - maxFundsStr, err := cmd.Flags().GetString(flagMaxFunds) - if err != nil { - return fmt.Errorf("max funds: %s", err) - } - - maxCalls, err := cmd.Flags().GetUint64(flagMaxCalls) - if err != nil { - return err - } - - exp, err := cmd.Flags().GetInt64(flagExpiration) - if err != nil { - return err - } - if exp == 0 { - return errors.New("expiration must be set") - } - - allowAllMsgs, err := cmd.Flags().GetBool(flagAllowAllMsgs) - if err != nil { - return err - } - - noTokenTransfer, err := cmd.Flags().GetBool(flagNoTokenTransfer) - if err != nil { - return err - } - - var limit types.ContractAuthzLimitX - switch { - case maxFundsStr != "" && maxCalls != 0 && !noTokenTransfer: - maxFunds, err := sdk.ParseCoinsNormalized(maxFundsStr) + var authorization authz.Authorization + switch args[1] { + case "execution": + limit, filter, err := parseGrantExecutionFlags(cmd) if err != nil { - return fmt.Errorf("max funds: %s", err) + return err } - limit = types.NewCombinedLimit(maxCalls, maxFunds...) - case maxFundsStr != "" && maxCalls == 0 && !noTokenTransfer: - maxFunds, err := sdk.ParseCoinsNormalized(maxFundsStr) + contract, err := sdk.AccAddressFromBech32(args[2]) if err != nil { - return fmt.Errorf("max funds: %s", err) + return err } - limit = types.NewMaxFundsLimit(maxFunds...) - case maxCalls != 0 && noTokenTransfer && maxFundsStr == "": - limit = types.NewMaxCallsLimit(maxCalls) - default: - return errors.New("invalid limit setup") - } - - var filter types.ContractAuthzFilterX - switch { - case allowAllMsgs && len(msgKeys) != 0 || allowAllMsgs && len(rawMsgs) != 0 || len(msgKeys) != 0 && len(rawMsgs) != 0: - return errors.New("cannot set more than one filter within one grant") - case allowAllMsgs: - filter = types.NewAllowAllMessagesFilter() - case len(msgKeys) != 0: - filter = types.NewAcceptedMessageKeysFilter(msgKeys...) - case len(rawMsgs) != 0: - msgs := make([]types.RawContractMessage, len(rawMsgs)) - for i, msg := range rawMsgs { - msgs[i] = types.RawContractMessage(msg) + grant, err := types.NewContractGrant(contract, limit, filter) + if err != nil { + return err } - filter = types.NewAcceptedMessagesFilter(msgs...) - default: - return errors.New("invalid filter setup") - } - - grant, err := types.NewContractGrant(contract, limit, filter) - if err != nil { - return err - } - - var authorization authz.Authorization - switch args[1] { - case "execution": authorization = types.NewContractExecutionAuthorization(*grant) case "migration": + limit, filter, err := parseGrantExecutionFlags(cmd) + if err != nil { + return err + } + contract, err := sdk.AccAddressFromBech32(args[2]) + if err != nil { + return err + } + grant, err := types.NewContractGrant(contract, limit, filter) + if err != nil { + return err + } authorization = types.NewContractMigrationAuthorization(*grant) + case "store-code": + grants, err := parseStoreCodeGrants(args[2:]) + if err != nil { + return err + } + authorization = types.NewStoreCodeAuthorization(grants...) default: return fmt.Errorf("%s authorization type not supported", args[1]) } @@ -558,50 +505,83 @@ $ %s tx grant execution --allow-all-messages --ma return cmd } -func GrantStoreCodeAuthorizationCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "grant-store-code [grantee] [code_hash:permission]", - Short: "Grant authorization to an address", - Long: fmt.Sprintf(`Grant authorization to an address. -Examples: -$ %s tx grant-store-code 13a1fc994cc6d1c81b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2a5:everybody 1wqrtry681b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2a5:nobody --expiration 1667979596 +func parseGrantExecutionFlags(cmd *cobra.Command) (types.ContractAuthzLimitX, types.ContractAuthzFilterX, error) { + msgKeys, err := cmd.Flags().GetStringSlice(flagAllowedMsgKeys) + if err != nil { + return nil, nil, err + } -$ %s tx grant-store-code *:%s1l2rsakp388kuv9k8qzq6lrm9taddae7fpx59wm,%s1vx8knpllrj7n963p9ttd80w47kpacrhuts497x -`, version.AppName, version.AppName, version.AppName, version.AppName), - Args: cobra.MinimumNArgs(2), - RunE: func(cmd *cobra.Command, args []string) error { - clientCtx, err := client.GetClientTxContext(cmd) - if err != nil { - return err - } + rawMsgs, err := cmd.Flags().GetStringSlice(flagAllowedRawMsgs) + if err != nil { + return nil, nil, err + } - grantee, err := sdk.AccAddressFromBech32(args[0]) - if err != nil { - return err - } + maxFundsStr, err := cmd.Flags().GetString(flagMaxFunds) + if err != nil { + return nil, nil, fmt.Errorf("max funds: %s", err) + } - grants, err := parseStoreCodeGrants(args[1:]) - if err != nil { - return err - } + maxCalls, err := cmd.Flags().GetUint64(flagMaxCalls) + if err != nil { + return nil, nil, err + } - authorization := types.NewStoreCodeAuthorization(grants...) + exp, err := cmd.Flags().GetInt64(flagExpiration) + if err != nil { + return nil, nil, err + } + if exp == 0 { + return nil, nil, errors.New("expiration must be set") + } - expire, err := getExpireTime(cmd) - if err != nil { - return err - } + allowAllMsgs, err := cmd.Flags().GetBool(flagAllowAllMsgs) + if err != nil { + return nil, nil, err + } - grantMsg, err := authz.NewMsgGrant(clientCtx.GetFromAddress(), grantee, authorization, expire) - if err != nil { - return err - } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), grantMsg) - }, + noTokenTransfer, err := cmd.Flags().GetBool(flagNoTokenTransfer) + if err != nil { + return nil, nil, err } - flags.AddTxFlagsToCmd(cmd) - cmd.Flags().Int64(flagExpiration, 0, "The Unix timestamp.") - return cmd + + var limit types.ContractAuthzLimitX + switch { + case maxFundsStr != "" && maxCalls != 0 && !noTokenTransfer: + maxFunds, err := sdk.ParseCoinsNormalized(maxFundsStr) + if err != nil { + return nil, nil, fmt.Errorf("max funds: %s", err) + } + limit = types.NewCombinedLimit(maxCalls, maxFunds...) + case maxFundsStr != "" && maxCalls == 0 && !noTokenTransfer: + maxFunds, err := sdk.ParseCoinsNormalized(maxFundsStr) + if err != nil { + return nil, nil, fmt.Errorf("max funds: %s", err) + } + limit = types.NewMaxFundsLimit(maxFunds...) + case maxCalls != 0 && noTokenTransfer && maxFundsStr == "": + limit = types.NewMaxCallsLimit(maxCalls) + default: + return nil, nil, errors.New("invalid limit setup") + } + + var filter types.ContractAuthzFilterX + switch { + case allowAllMsgs && len(msgKeys) != 0 || allowAllMsgs && len(rawMsgs) != 0 || len(msgKeys) != 0 && len(rawMsgs) != 0: + return nil, nil, errors.New("cannot set more than one filter within one grant") + case allowAllMsgs: + filter = types.NewAllowAllMessagesFilter() + case len(msgKeys) != 0: + filter = types.NewAcceptedMessageKeysFilter(msgKeys...) + case len(rawMsgs) != 0: + msgs := make([]types.RawContractMessage, len(rawMsgs)) + for i, msg := range rawMsgs { + msgs[i] = types.RawContractMessage(msg) + } + filter = types.NewAcceptedMessagesFilter(msgs...) + default: + return nil, nil, errors.New("invalid filter setup") + } + return limit, filter, nil } func getExpireTime(cmd *cobra.Command) (*time.Time, error) { From c5a9537a83f092b85b50b2cf7cc1be60b903123a Mon Sep 17 00:00:00 2001 From: Alexander Peters Date: Fri, 15 Sep 2023 15:27:32 +0200 Subject: [PATCH 5/5] Revisit CLI and system tests (#1627) * Restructure CLI; fix system test * Review feedback --- tests/system/cli.go | 17 +- tests/system/permissioned_test.go | 20 +-- tests/system/system.go | 1 + x/wasm/client/cli/tx.go | 251 +++++++++++++++++------------- 4 files changed, 169 insertions(+), 120 deletions(-) diff --git a/tests/system/cli.go b/tests/system/cli.go index 8d0ab87f9e..7aeddb38ab 100644 --- a/tests/system/cli.go +++ b/tests/system/cli.go @@ -2,6 +2,7 @@ package system import ( "fmt" + "io" "os/exec" "path/filepath" "strconv" @@ -196,6 +197,10 @@ func (c WasmdCli) CustomQuery(args ...string) string { // execute shell command func (c WasmdCli) run(args []string) (output string, ok bool) { + return c.runWithInput(args, nil) +} + +func (c WasmdCli) runWithInput(args []string, input io.Reader) (output string, ok bool) { if c.Debug { c.t.Logf("+++ running `%s %s`", c.execBinary, strings.Join(args, " ")) } @@ -207,6 +212,7 @@ func (c WasmdCli) run(args []string) (output string, ok bool) { }() cmd := exec.Command(locateExecutable("wasmd"), args...) //nolint:gosec cmd.Dir = workDir + cmd.Stdin = input return cmd.CombinedOutput() }() ok = c.assertErrorFn(c.t, gotErr, string(gotOut)) @@ -256,13 +262,22 @@ func (c WasmdCli) WasmExecute(contractAddr, msg, from string, args ...string) st // AddKey add key to default keyring. Returns address func (c WasmdCli) AddKey(name string) string { - cmd := c.withKeyringFlags("keys", "add", name, "--no-backup") + cmd := c.withKeyringFlags("keys", "add", name) //, "--no-backup") out, _ := c.run(cmd) addr := gjson.Get(out, "address").String() require.NotEmpty(c.t, addr, "got %q", out) return addr } +// AddKeyFromSeed recovers the key from given seed and add it to default keyring. Returns address +func (c WasmdCli) AddKeyFromSeed(name, mnemoic string) string { + cmd := c.withKeyringFlags("keys", "add", name, "--recover") + out, _ := c.runWithInput(cmd, strings.NewReader(mnemoic)) + addr := gjson.Get(out, "address").String() + require.NotEmpty(c.t, addr, "got %q", out) + return addr +} + // GetKeyAddr returns address func (c WasmdCli) GetKeyAddr(name string) string { cmd := c.withKeyringFlags("keys", "show", name, "-a") diff --git a/tests/system/permissioned_test.go b/tests/system/permissioned_test.go index 92470a9f9b..b2da9b6444 100644 --- a/tests/system/permissioned_test.go +++ b/tests/system/permissioned_test.go @@ -12,17 +12,17 @@ import ( ) func TestGrantStoreCodePermissionedChain(t *testing.T) { - sut.ResetChain(t) cli := NewWasmdCLI(t, sut, verbose) + // set params to restrict chain + const chainAuthorityAddress = "wasm1pvuujjdk0xt043ga0j9nrfh5u8pzj4rpplyqkm" + sut.ModifyGenesisJSON(t, SetCodeUploadPermission(t, "AnyOfAddresses", chainAuthorityAddress)) - chainAuthorizedAccount := cli.AddKey("chain_authorized_account") + recoveredAddress := cli.AddKeyFromSeed("chain_authority", "aisle ship absurd wedding arch admit fringe foam cluster tide trim aisle salad shiver tackle palm glance wrist valley hamster couch crystal frozen chronic") + require.Equal(t, chainAuthorityAddress, recoveredAddress) devAccount := cli.AddKey("dev_account") - //set params - sut.ModifyGenesisJSON(t, SetCodeUploadPermission(t, "AnyOfAddresses", chainAuthorizedAccount)) - sut.ModifyGenesisCLI(t, - []string{"genesis", "add-genesis-account", chainAuthorizedAccount, "100000000stake"}, + []string{"genesis", "add-genesis-account", chainAuthorityAddress, "100000000stake"}, ) sut.ModifyGenesisCLI(t, []string{"genesis", "add-genesis-account", devAccount, "100000000stake"}, @@ -37,10 +37,10 @@ func TestGrantStoreCodePermissionedChain(t *testing.T) { require.Equal(t, 1, len(addrRes)) require.Equal(t, permission, "AnyOfAddresses") - require.Equal(t, chainAuthorizedAccount, addrRes[0].Str) + require.Equal(t, chainAuthorityAddress, addrRes[0].Str) - // chain_authorized_account grant upload permission to dev_account - rsp = cli.CustomCommand("tx", "wasm", "grant", devAccount, "store-code", "*:*", "--from="+chainAuthorizedAccount) + // chain_authority grant upload permission to dev_account + rsp = cli.CustomCommand("tx", "wasm", "grant", "store-code", devAccount, "*:*", "--from="+chainAuthorityAddress) RequireTxSuccess(t, rsp) // dev_account store code fails as the address is not in the code-upload accept-list @@ -48,7 +48,7 @@ func TestGrantStoreCodePermissionedChain(t *testing.T) { RequireTxFailure(t, rsp) // create tx should work for addresses in the accept-list - args := cli.withTXFlags("tx", "wasm", "store", "./testdata/hackatom.wasm.gzip", "--from="+chainAuthorizedAccount, "--generate-only") + args := cli.withTXFlags("tx", "wasm", "store", "./testdata/hackatom.wasm.gzip", "--from="+chainAuthorityAddress, "--generate-only") tx, ok := cli.run(args) require.True(t, ok) diff --git a/tests/system/system.go b/tests/system/system.go index 47cbdf1425..89b5ca0543 100644 --- a/tests/system/system.go +++ b/tests/system/system.go @@ -390,6 +390,7 @@ type GenesisMutator func([]byte) []byte // return state // } func (s *SystemUnderTest) ModifyGenesisJSON(t *testing.T, mutators ...GenesisMutator) { + s.ResetChain(t) s.modifyGenesisJSON(t, mutators...) } diff --git a/x/wasm/client/cli/tx.go b/x/wasm/client/cli/tx.go index c2e134751e..863730e9f2 100644 --- a/x/wasm/client/cli/tx.go +++ b/x/wasm/client/cli/tx.go @@ -67,7 +67,7 @@ func GetTxCmd() *cobra.Command { MigrateContractCmd(), UpdateContractAdminCmd(), ClearContractAdminCmd(), - GrantAuthorizationCmd(), + GrantCmd(), UpdateInstantiateConfigCmd(), SubmitProposalCmd(), ) @@ -414,22 +414,32 @@ func parseExecuteArgs(contractAddr, execMsg string, sender sdk.AccAddress, flags }, nil } +func GrantCmd() *cobra.Command { + txCmd := &cobra.Command{ + Use: "grant", + Short: "Grant a authz permission", + DisableFlagParsing: true, + SilenceUsage: true, + } + txCmd.AddCommand( + GrantAuthorizationCmd(), + GrantStoreCodeAuthorizationCmd(), + ) + return txCmd +} + func GrantAuthorizationCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "grant [grantee] [message_type=\"execution\"|\"migration\"|\"store-code\"]", - Short: "Grant authorization to an address", + Use: "contract [message_type=\"execution\"|\"migration\"] [grantee] [contract_addr_bech32] --allow-raw-msgs [msg1,msg2,...] --allow-msg-keys [key1,key2,...] --allow-all-messages", + Short: "Grant authorization to interact with a contract on behalf of you", Long: fmt.Sprintf(`Grant authorization to an address. Examples: -$ %s tx grant execution --allow-all-messages --max-calls 1 --no-token-transfer --expiration 1667979596 - -$ %s tx grant execution --allow-all-messages --max-funds 100000uwasm --expiration 1667979596 +$ %s tx grant contract execution --allow-all-messages --max-calls 1 --no-token-transfer --expiration 1667979596 -$ %s tx grant execution --allow-all-messages --max-calls 5 --max-funds 100000uwasm --expiration 1667979596 +$ %s tx grant contract execution --allow-all-messages --max-funds 100000uwasm --expiration 1667979596 -$ %s tx grant store-code 13a1fc994cc6d1c81b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2a5:everybody 1wqrtry681b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2a5:nobody --expiration 1667979596 - -$ %s tx grant store-code *:%s1l2rsakp388kuv9k8qzq6lrm9taddae7fpx59wm,%s1vx8knpllrj7n963p9ttd80w47kpacrhuts497x -`, version.AppName, version.AppName, version.AppName, version.AppName, version.AppName, version.AppName, version.AppName), +$ %s tx grant contract execution --allow-all-messages --max-calls 5 --max-funds 100000uwasm --expiration 1667979596 +`, version.AppName, version.AppName, version.AppName), Args: cobra.ExactArgs(3), RunE: func(cmd *cobra.Command, args []string) error { clientCtx, err := client.GetClientTxContext(cmd) @@ -442,42 +452,98 @@ $ %s tx grant store-code *:%s1l2rsakp388kuv9k8qzq6lrm9taddae7fpx5 return err } - var authorization authz.Authorization - switch args[1] { - case "execution": - limit, filter, err := parseGrantExecutionFlags(cmd) + contract, err := sdk.AccAddressFromBech32(args[2]) + if err != nil { + return err + } + + msgKeys, err := cmd.Flags().GetStringSlice(flagAllowedMsgKeys) + if err != nil { + return err + } + + rawMsgs, err := cmd.Flags().GetStringSlice(flagAllowedRawMsgs) + if err != nil { + return err + } + + maxFundsStr, err := cmd.Flags().GetString(flagMaxFunds) + if err != nil { + return fmt.Errorf("max funds: %s", err) + } + + maxCalls, err := cmd.Flags().GetUint64(flagMaxCalls) + if err != nil { + return err + } + + exp, err := cmd.Flags().GetInt64(flagExpiration) + if err != nil { + return err + } + if exp == 0 { + return errors.New("expiration must be set") + } + + allowAllMsgs, err := cmd.Flags().GetBool(flagAllowAllMsgs) + if err != nil { + return err + } + + noTokenTransfer, err := cmd.Flags().GetBool(flagNoTokenTransfer) + if err != nil { + return err + } + + var limit types.ContractAuthzLimitX + switch { + case maxFundsStr != "" && maxCalls != 0 && !noTokenTransfer: + maxFunds, err := sdk.ParseCoinsNormalized(maxFundsStr) if err != nil { - return err + return fmt.Errorf("max funds: %s", err) } - contract, err := sdk.AccAddressFromBech32(args[2]) + limit = types.NewCombinedLimit(maxCalls, maxFunds...) + case maxFundsStr != "" && maxCalls == 0 && !noTokenTransfer: + maxFunds, err := sdk.ParseCoinsNormalized(maxFundsStr) if err != nil { - return err + return fmt.Errorf("max funds: %s", err) } - grant, err := types.NewContractGrant(contract, limit, filter) - if err != nil { - return err + limit = types.NewMaxFundsLimit(maxFunds...) + case maxCalls != 0 && noTokenTransfer && maxFundsStr == "": + limit = types.NewMaxCallsLimit(maxCalls) + default: + return errors.New("invalid limit setup") + } + + var filter types.ContractAuthzFilterX + switch { + case allowAllMsgs && len(msgKeys) != 0 || allowAllMsgs && len(rawMsgs) != 0 || len(msgKeys) != 0 && len(rawMsgs) != 0: + return errors.New("cannot set more than one filter within one grant") + case allowAllMsgs: + filter = types.NewAllowAllMessagesFilter() + case len(msgKeys) != 0: + filter = types.NewAcceptedMessageKeysFilter(msgKeys...) + case len(rawMsgs) != 0: + msgs := make([]types.RawContractMessage, len(rawMsgs)) + for i, msg := range rawMsgs { + msgs[i] = types.RawContractMessage(msg) } + filter = types.NewAcceptedMessagesFilter(msgs...) + default: + return errors.New("invalid filter setup") + } + + grant, err := types.NewContractGrant(contract, limit, filter) + if err != nil { + return err + } + + var authorization authz.Authorization + switch args[1] { + case "execution": authorization = types.NewContractExecutionAuthorization(*grant) case "migration": - limit, filter, err := parseGrantExecutionFlags(cmd) - if err != nil { - return err - } - contract, err := sdk.AccAddressFromBech32(args[2]) - if err != nil { - return err - } - grant, err := types.NewContractGrant(contract, limit, filter) - if err != nil { - return err - } authorization = types.NewContractMigrationAuthorization(*grant) - case "store-code": - grants, err := parseStoreCodeGrants(args[2:]) - if err != nil { - return err - } - authorization = types.NewStoreCodeAuthorization(grants...) default: return fmt.Errorf("%s authorization type not supported", args[1]) } @@ -505,83 +571,50 @@ $ %s tx grant store-code *:%s1l2rsakp388kuv9k8qzq6lrm9taddae7fpx5 return cmd } -func parseGrantExecutionFlags(cmd *cobra.Command) (types.ContractAuthzLimitX, types.ContractAuthzFilterX, error) { - msgKeys, err := cmd.Flags().GetStringSlice(flagAllowedMsgKeys) - if err != nil { - return nil, nil, err - } - - rawMsgs, err := cmd.Flags().GetStringSlice(flagAllowedRawMsgs) - if err != nil { - return nil, nil, err - } +func GrantStoreCodeAuthorizationCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "store-code [grantee] [code_hash:permission]", + Short: "Grant authorization to upload contract code on behalf of you", + Long: fmt.Sprintf(`Grant authorization to an address. +Examples: +$ %s tx grant store-code 13a1fc994cc6d1c81b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2a5:everybody 1wqrtry681b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2a5:nobody --expiration 1667979596 - maxFundsStr, err := cmd.Flags().GetString(flagMaxFunds) - if err != nil { - return nil, nil, fmt.Errorf("max funds: %s", err) - } +$ %s tx grant store-code *:%s1l2rsakp388kuv9k8qzq6lrm9taddae7fpx59wm,%s1vx8knpllrj7n963p9ttd80w47kpacrhuts497x +`, version.AppName, version.AppName, version.AppName, version.AppName), + Args: cobra.MinimumNArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } - maxCalls, err := cmd.Flags().GetUint64(flagMaxCalls) - if err != nil { - return nil, nil, err - } + grantee, err := sdk.AccAddressFromBech32(args[0]) + if err != nil { + return err + } - exp, err := cmd.Flags().GetInt64(flagExpiration) - if err != nil { - return nil, nil, err - } - if exp == 0 { - return nil, nil, errors.New("expiration must be set") - } + grants, err := parseStoreCodeGrants(args[1:]) + if err != nil { + return err + } - allowAllMsgs, err := cmd.Flags().GetBool(flagAllowAllMsgs) - if err != nil { - return nil, nil, err - } + authorization := types.NewStoreCodeAuthorization(grants...) - noTokenTransfer, err := cmd.Flags().GetBool(flagNoTokenTransfer) - if err != nil { - return nil, nil, err - } + expire, err := getExpireTime(cmd) + if err != nil { + return err + } - var limit types.ContractAuthzLimitX - switch { - case maxFundsStr != "" && maxCalls != 0 && !noTokenTransfer: - maxFunds, err := sdk.ParseCoinsNormalized(maxFundsStr) - if err != nil { - return nil, nil, fmt.Errorf("max funds: %s", err) - } - limit = types.NewCombinedLimit(maxCalls, maxFunds...) - case maxFundsStr != "" && maxCalls == 0 && !noTokenTransfer: - maxFunds, err := sdk.ParseCoinsNormalized(maxFundsStr) - if err != nil { - return nil, nil, fmt.Errorf("max funds: %s", err) - } - limit = types.NewMaxFundsLimit(maxFunds...) - case maxCalls != 0 && noTokenTransfer && maxFundsStr == "": - limit = types.NewMaxCallsLimit(maxCalls) - default: - return nil, nil, errors.New("invalid limit setup") - } - - var filter types.ContractAuthzFilterX - switch { - case allowAllMsgs && len(msgKeys) != 0 || allowAllMsgs && len(rawMsgs) != 0 || len(msgKeys) != 0 && len(rawMsgs) != 0: - return nil, nil, errors.New("cannot set more than one filter within one grant") - case allowAllMsgs: - filter = types.NewAllowAllMessagesFilter() - case len(msgKeys) != 0: - filter = types.NewAcceptedMessageKeysFilter(msgKeys...) - case len(rawMsgs) != 0: - msgs := make([]types.RawContractMessage, len(rawMsgs)) - for i, msg := range rawMsgs { - msgs[i] = types.RawContractMessage(msg) - } - filter = types.NewAcceptedMessagesFilter(msgs...) - default: - return nil, nil, errors.New("invalid filter setup") + grantMsg, err := authz.NewMsgGrant(clientCtx.GetFromAddress(), grantee, authorization, expire) + if err != nil { + return err + } + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), grantMsg) + }, } - return limit, filter, nil + flags.AddTxFlagsToCmd(cmd) + cmd.Flags().Int64(flagExpiration, 0, "The Unix timestamp.") + return cmd } func getExpireTime(cmd *cobra.Command) (*time.Time, error) {