Skip to content

Commit 76c0c06

Browse files
Validate number of addresses in msg (#1926)
Co-authored-by: Christoph Otter <[email protected]>
1 parent 745adca commit 76c0c06

File tree

4 files changed

+96
-49
lines changed

4 files changed

+96
-49
lines changed

x/wasm/types/params.go

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ func (a AccessType) With(addrs ...sdk.AccAddress) AccessConfig {
2929
for i, v := range addrs {
3030
bech32Addrs[i] = v.String()
3131
}
32-
if err := assertValidAddresses(bech32Addrs); err != nil {
32+
if err := validateBech32Addresses(bech32Addrs); err != nil {
3333
panic(errorsmod.Wrap(err, "addresses"))
3434
}
3535
return AccessConfig{Permission: AccessTypeAnyOfAddresses, Addresses: bech32Addrs}
@@ -129,28 +129,11 @@ func (a AccessConfig) ValidateBasic() error {
129129
case AccessTypeNobody, AccessTypeEverybody:
130130
return nil
131131
case AccessTypeAnyOfAddresses:
132-
return errorsmod.Wrap(assertValidAddresses(a.Addresses), "addresses")
132+
return errorsmod.Wrap(validateBech32Addresses(a.Addresses), "addresses")
133133
}
134134
return errorsmod.Wrapf(ErrInvalid, "unknown type: %q", a.Permission)
135135
}
136136

137-
func assertValidAddresses(addrs []string) error {
138-
if len(addrs) == 0 {
139-
return ErrEmpty
140-
}
141-
idx := make(map[string]struct{}, len(addrs))
142-
for _, a := range addrs {
143-
if _, err := sdk.AccAddressFromBech32(a); err != nil {
144-
return errorsmod.Wrapf(err, "address: %s", a)
145-
}
146-
if _, exists := idx[a]; exists {
147-
return ErrDuplicate.Wrapf("address: %s", a)
148-
}
149-
idx[a] = struct{}{}
150-
}
151-
return nil
152-
}
153-
154137
// Allowed returns if permission includes the actor.
155138
// Actor address must be valid and not nil
156139
func (a AccessConfig) Allowed(actor sdk.AccAddress) bool {

x/wasm/types/tx.go

Lines changed: 8 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import (
1212
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
1313
)
1414

15+
const maxCodeIDCount = 50
16+
1517
// RawContractMessage defines a json message that is sent or returned by a wasm contract.
1618
// This type can hold any type of bytes. Until validateBasic is called there should not be
1719
// any assumptions made that the data is valid syntax or semantic.
@@ -356,15 +358,14 @@ func (msg MsgPinCodes) ValidateBasic() error {
356358
return validateCodeIDs(msg.CodeIDs)
357359
}
358360

359-
const maxCodeIDTotal = 50
360-
361-
// ensure not empty, not duplicates and not exceeding max number
361+
// validateCodeIDs ensures the list is not empty, has no duplicates
362+
// and does not exceed the max number of code IDs
362363
func validateCodeIDs(codeIDs []uint64) error {
363364
switch n := len(codeIDs); {
364365
case n == 0:
365366
return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "empty code ids")
366-
case n > maxCodeIDTotal:
367-
return errorsmod.Wrapf(sdkerrors.ErrInvalidRequest, "total number of code ids is greater than %d", maxCodeIDTotal)
367+
case n > maxCodeIDCount:
368+
return errorsmod.Wrapf(sdkerrors.ErrInvalidRequest, "total number of code ids is greater than %d", maxCodeIDCount)
368369
}
369370
if hasDuplicates(codeIDs) {
370371
return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "duplicate code ids")
@@ -468,11 +469,7 @@ func (msg MsgAddCodeUploadParamsAddresses) ValidateBasic() error {
468469
return errorsmod.Wrap(err, "authority")
469470
}
470471

471-
if len(msg.Addresses) == 0 {
472-
return errorsmod.Wrap(ErrEmpty, "addresses")
473-
}
474-
475-
return checkDuplicatedAddresses(msg.Addresses)
472+
return validateBech32Addresses(msg.Addresses)
476473
}
477474

478475
func (msg MsgRemoveCodeUploadParamsAddresses) Route() string {
@@ -488,26 +485,7 @@ func (msg MsgRemoveCodeUploadParamsAddresses) ValidateBasic() error {
488485
return errorsmod.Wrap(err, "authority")
489486
}
490487

491-
if len(msg.Addresses) == 0 {
492-
return errorsmod.Wrap(ErrEmpty, "addresses")
493-
}
494-
495-
return checkDuplicatedAddresses(msg.Addresses)
496-
}
497-
498-
func checkDuplicatedAddresses(addresses []string) error {
499-
index := map[string]struct{}{}
500-
for _, addr := range addresses {
501-
addr = strings.ToUpper(addr)
502-
if _, err := sdk.AccAddressFromBech32(addr); err != nil {
503-
return errorsmod.Wrap(err, "addresses")
504-
}
505-
if _, found := index[addr]; found {
506-
return errorsmod.Wrap(ErrInvalid, "duplicate addresses")
507-
}
508-
index[addr] = struct{}{}
509-
}
510-
return nil
488+
return validateBech32Addresses(msg.Addresses)
511489
}
512490

513491
func (msg MsgStoreAndMigrateContract) Route() string {

x/wasm/types/tx_test.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -763,6 +763,13 @@ func TestMsgAddCodeUploadParamsAddressesValidation(t *testing.T) {
763763
badAddress := "abcd"
764764
// proper address size
765765
goodAddress := sdk.AccAddress(make([]byte, 20)).String()
766+
goodAddress2 := strings.ToUpper(goodAddress)
767+
require.NotEqual(t, goodAddress, goodAddress2) // sanity check
768+
769+
tooManyAddresses := make([]string, MaxAddressCount+1)
770+
for i := range tooManyAddresses {
771+
tooManyAddresses[i] = sdk.AccAddress(bytes.Repeat([]byte{byte(i)}, 20)).String()
772+
}
766773

767774
specs := map[string]struct {
768775
src MsgAddCodeUploadParamsAddresses
@@ -774,6 +781,12 @@ func TestMsgAddCodeUploadParamsAddressesValidation(t *testing.T) {
774781
Addresses: []string{goodAddress},
775782
},
776783
},
784+
"all good, uppercase": {
785+
src: MsgAddCodeUploadParamsAddresses{
786+
Authority: goodAddress,
787+
Addresses: []string{goodAddress2},
788+
},
789+
},
777790
"bad authority": {
778791
src: MsgAddCodeUploadParamsAddresses{
779792
Authority: badAddress,
@@ -807,6 +820,20 @@ func TestMsgAddCodeUploadParamsAddressesValidation(t *testing.T) {
807820
},
808821
expErr: true,
809822
},
823+
"duplicate addresses, different casing": {
824+
src: MsgAddCodeUploadParamsAddresses{
825+
Authority: goodAddress,
826+
Addresses: []string{goodAddress, goodAddress2},
827+
},
828+
expErr: true,
829+
},
830+
"too many addresses": {
831+
src: MsgAddCodeUploadParamsAddresses{
832+
Authority: goodAddress,
833+
Addresses: tooManyAddresses,
834+
},
835+
expErr: true,
836+
},
810837
}
811838
for msg, spec := range specs {
812839
t.Run(msg, func(t *testing.T) {
@@ -823,6 +850,13 @@ func TestMsgAddCodeUploadParamsAddressesValidation(t *testing.T) {
823850
func TestMsgRemoveCodeUploadParamsAddressesValidation(t *testing.T) {
824851
// proper address size
825852
goodAddress := sdk.AccAddress(make([]byte, 20)).String()
853+
goodAddress2 := strings.ToUpper(goodAddress)
854+
require.NotEqual(t, goodAddress, goodAddress2) // sanity check
855+
856+
tooManyAddresses := make([]string, MaxAddressCount+1)
857+
for i := range tooManyAddresses {
858+
tooManyAddresses[i] = sdk.AccAddress(bytes.Repeat([]byte{byte(i)}, 20)).String()
859+
}
826860

827861
specs := map[string]struct {
828862
src MsgRemoveCodeUploadParamsAddresses
@@ -834,6 +868,12 @@ func TestMsgRemoveCodeUploadParamsAddressesValidation(t *testing.T) {
834868
Addresses: []string{goodAddress},
835869
},
836870
},
871+
"all good, uppercase": {
872+
src: MsgRemoveCodeUploadParamsAddresses{
873+
Authority: goodAddress,
874+
Addresses: []string{goodAddress2},
875+
},
876+
},
837877
"bad authority": {
838878
src: MsgRemoveCodeUploadParamsAddresses{
839879
Authority: badAddress,
@@ -867,6 +907,20 @@ func TestMsgRemoveCodeUploadParamsAddressesValidation(t *testing.T) {
867907
},
868908
expErr: true,
869909
},
910+
"duplicate addresses, different casing": {
911+
src: MsgRemoveCodeUploadParamsAddresses{
912+
Authority: goodAddress,
913+
Addresses: []string{goodAddress, goodAddress2},
914+
},
915+
expErr: true,
916+
},
917+
"too many addresses": {
918+
src: MsgRemoveCodeUploadParamsAddresses{
919+
Authority: goodAddress,
920+
Addresses: tooManyAddresses,
921+
},
922+
expErr: true,
923+
},
870924
}
871925
for msg, spec := range specs {
872926
t.Run(msg, func(t *testing.T) {

x/wasm/types/validation.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ import (
99
"github.com/distribution/reference"
1010

1111
errorsmod "cosmossdk.io/errors"
12+
13+
sdk "github.com/cosmos/cosmos-sdk/types"
14+
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
1215
)
1316

1417
// MaxSaltSize is the longest salt that can be used when instantiating a contract
@@ -23,6 +26,9 @@ var (
2326

2427
// MaxProposalWasmSize is the largest a gov proposal compiled contract code can be when storing code on chain
2528
MaxProposalWasmSize = 3 * 1024 * 1024 // extension point for chains to customize via compile flag.
29+
30+
// MaxAddressCount is the maximum number of addresses allowed within a message
31+
MaxAddressCount = 50
2632
)
2733

2834
func validateWasmCode(s []byte, maxSize int) error {
@@ -92,3 +98,29 @@ func ValidateVerificationInfo(source, builder string, codeHash []byte) error {
9298
}
9399
return nil
94100
}
101+
102+
// validateBech32Addresses ensures the list is not empty, has no duplicates
103+
// and does not exceed the max number of addresses
104+
func validateBech32Addresses(addresses []string) error {
105+
switch n := len(addresses); {
106+
case n == 0:
107+
return errorsmod.Wrap(ErrEmpty, "addresses")
108+
case n > MaxAddressCount:
109+
return errorsmod.Wrapf(sdkerrors.ErrInvalidRequest, "total number of addresses is greater than %d", MaxAddressCount)
110+
}
111+
112+
index := map[string]struct{}{}
113+
for _, addr := range addresses {
114+
if _, err := sdk.AccAddressFromBech32(addr); err != nil {
115+
return errorsmod.Wrapf(err, "address: %s", addr)
116+
}
117+
// Bech32 addresses are case-insensitive, i.e. the same address can have multiple representations,
118+
// so we normalize here to avoid duplicates.
119+
addr = strings.ToUpper(addr)
120+
if _, found := index[addr]; found {
121+
return errorsmod.Wrap(ErrDuplicate, "duplicate addresses")
122+
}
123+
index[addr] = struct{}{}
124+
}
125+
return nil
126+
}

0 commit comments

Comments
 (0)