feat: Add x/authz SendAuthorization AllowList (#12648)

## Description

Closes: #12609 



---

### Author Checklist

*All items are required. Please add a note to the item if the item is not applicable and
please add links to any relevant follow up issues.*

I have...

- [ ] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
- [ ] added `!` to the type prefix if API or client breaking change
- [ ] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/main/CONTRIBUTING.md#pr-targeting))
- [ ] provided a link to the relevant issue or specification
- [ ] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/main/docs/building-modules)
- [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/main/CONTRIBUTING.md#testing)
- [ ] added a changelog entry to `CHANGELOG.md`
- [ ] included comments for [documenting Go code](https://blog.golang.org/godoc)
- [ ] updated the relevant documentation or specification
- [ ] reviewed "Files changed" and left comments if necessary
- [ ] confirmed all CI checks have passed

### Reviewers Checklist

*All items are required. Please add a note if the item is not applicable and please add
your handle next to the items reviewed if you only reviewed selected items.*

I have...

- [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
- [ ] confirmed `!` in the type prefix if API or client breaking change
- [ ] confirmed all author checklist items have been addressed 
- [ ] reviewed state machine logic
- [ ] reviewed API design and naming
- [ ] reviewed documentation is accurate
- [ ] reviewed tests and test coverage
- [ ] manually tested (if applicable)
This commit is contained in:
likhita-809 2022-07-27 15:47:47 +05:30 committed by GitHub
parent df4339cd16
commit 9f5ee97889
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 514 additions and 59 deletions

View File

@ -39,6 +39,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
### Features
* (x/authz) [#12648](https://github.com/cosmos/cosmos-sdk/pull/12648) Add an allow list, an optional list of addresses allowed to receive bank assests via authz MsgSend grant.
* (cli) [#12028](https://github.com/cosmos/cosmos-sdk/pull/12028) Add the `tendermint key-migrate` to perform Tendermint v0.35 DB key migration.
* (sdk.Coins) [#12627](https://github.com/cosmos/cosmos-sdk/pull/12627) Make a Denoms method on sdk.Coins.
@ -63,6 +64,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
### API Breaking Changes
* (x/bank) [#12648](https://github.com/cosmos/cosmos-sdk/pull/12648) `NewSendAuthorization` takes a new argument of an optional list of addresses allowed to receive bank assests via authz MsgSend grant. You can pass `nil` for the same behavior as before, i.e. any recipient is allowed.
* (x/bank) [\#12593](https://github.com/cosmos/cosmos-sdk/pull/12593) Add `SpendableCoin` method to `BaseViewKeeper`
* (x/slashing) [#12581](https://github.com/cosmos/cosmos-sdk/pull/12581) Remove `x/slashing` legacy querier.
* (types) [\#12355](https://github.com/cosmos/cosmos-sdk/pull/12355) Remove the compile-time `types.DBbackend` variable. Removes usage of the same in server/util.go

View File

@ -66,15 +66,63 @@ func (x *_SendAuthorization_1_list) IsValid() bool {
return x.list != nil
}
var _ protoreflect.List = (*_SendAuthorization_2_list)(nil)
type _SendAuthorization_2_list struct {
list *[]string
}
func (x *_SendAuthorization_2_list) Len() int {
if x.list == nil {
return 0
}
return len(*x.list)
}
func (x *_SendAuthorization_2_list) Get(i int) protoreflect.Value {
return protoreflect.ValueOfString((*x.list)[i])
}
func (x *_SendAuthorization_2_list) Set(i int, value protoreflect.Value) {
valueUnwrapped := value.String()
concreteValue := valueUnwrapped
(*x.list)[i] = concreteValue
}
func (x *_SendAuthorization_2_list) Append(value protoreflect.Value) {
valueUnwrapped := value.String()
concreteValue := valueUnwrapped
*x.list = append(*x.list, concreteValue)
}
func (x *_SendAuthorization_2_list) AppendMutable() protoreflect.Value {
panic(fmt.Errorf("AppendMutable can not be called on message SendAuthorization at list field AllowList as it is not of Message kind"))
}
func (x *_SendAuthorization_2_list) Truncate(n int) {
*x.list = (*x.list)[:n]
}
func (x *_SendAuthorization_2_list) NewElement() protoreflect.Value {
v := ""
return protoreflect.ValueOfString(v)
}
func (x *_SendAuthorization_2_list) IsValid() bool {
return x.list != nil
}
var (
md_SendAuthorization protoreflect.MessageDescriptor
fd_SendAuthorization_spend_limit protoreflect.FieldDescriptor
fd_SendAuthorization_allow_list protoreflect.FieldDescriptor
)
func init() {
file_cosmos_bank_v1beta1_authz_proto_init()
md_SendAuthorization = File_cosmos_bank_v1beta1_authz_proto.Messages().ByName("SendAuthorization")
fd_SendAuthorization_spend_limit = md_SendAuthorization.Fields().ByName("spend_limit")
fd_SendAuthorization_allow_list = md_SendAuthorization.Fields().ByName("allow_list")
}
var _ protoreflect.Message = (*fastReflection_SendAuthorization)(nil)
@ -148,6 +196,12 @@ func (x *fastReflection_SendAuthorization) Range(f func(protoreflect.FieldDescri
return
}
}
if len(x.AllowList) != 0 {
value := protoreflect.ValueOfList(&_SendAuthorization_2_list{list: &x.AllowList})
if !f(fd_SendAuthorization_allow_list, value) {
return
}
}
}
// Has reports whether a field is populated.
@ -165,6 +219,8 @@ func (x *fastReflection_SendAuthorization) Has(fd protoreflect.FieldDescriptor)
switch fd.FullName() {
case "cosmos.bank.v1beta1.SendAuthorization.spend_limit":
return len(x.SpendLimit) != 0
case "cosmos.bank.v1beta1.SendAuthorization.allow_list":
return len(x.AllowList) != 0
default:
if fd.IsExtension() {
panic(fmt.Errorf("proto3 declared messages do not support extensions: cosmos.bank.v1beta1.SendAuthorization"))
@ -183,6 +239,8 @@ func (x *fastReflection_SendAuthorization) Clear(fd protoreflect.FieldDescriptor
switch fd.FullName() {
case "cosmos.bank.v1beta1.SendAuthorization.spend_limit":
x.SpendLimit = nil
case "cosmos.bank.v1beta1.SendAuthorization.allow_list":
x.AllowList = nil
default:
if fd.IsExtension() {
panic(fmt.Errorf("proto3 declared messages do not support extensions: cosmos.bank.v1beta1.SendAuthorization"))
@ -205,6 +263,12 @@ func (x *fastReflection_SendAuthorization) Get(descriptor protoreflect.FieldDesc
}
listValue := &_SendAuthorization_1_list{list: &x.SpendLimit}
return protoreflect.ValueOfList(listValue)
case "cosmos.bank.v1beta1.SendAuthorization.allow_list":
if len(x.AllowList) == 0 {
return protoreflect.ValueOfList(&_SendAuthorization_2_list{})
}
listValue := &_SendAuthorization_2_list{list: &x.AllowList}
return protoreflect.ValueOfList(listValue)
default:
if descriptor.IsExtension() {
panic(fmt.Errorf("proto3 declared messages do not support extensions: cosmos.bank.v1beta1.SendAuthorization"))
@ -229,6 +293,10 @@ func (x *fastReflection_SendAuthorization) Set(fd protoreflect.FieldDescriptor,
lv := value.List()
clv := lv.(*_SendAuthorization_1_list)
x.SpendLimit = *clv.list
case "cosmos.bank.v1beta1.SendAuthorization.allow_list":
lv := value.List()
clv := lv.(*_SendAuthorization_2_list)
x.AllowList = *clv.list
default:
if fd.IsExtension() {
panic(fmt.Errorf("proto3 declared messages do not support extensions: cosmos.bank.v1beta1.SendAuthorization"))
@ -255,6 +323,12 @@ func (x *fastReflection_SendAuthorization) Mutable(fd protoreflect.FieldDescript
}
value := &_SendAuthorization_1_list{list: &x.SpendLimit}
return protoreflect.ValueOfList(value)
case "cosmos.bank.v1beta1.SendAuthorization.allow_list":
if x.AllowList == nil {
x.AllowList = []string{}
}
value := &_SendAuthorization_2_list{list: &x.AllowList}
return protoreflect.ValueOfList(value)
default:
if fd.IsExtension() {
panic(fmt.Errorf("proto3 declared messages do not support extensions: cosmos.bank.v1beta1.SendAuthorization"))
@ -271,6 +345,9 @@ func (x *fastReflection_SendAuthorization) NewField(fd protoreflect.FieldDescrip
case "cosmos.bank.v1beta1.SendAuthorization.spend_limit":
list := []*v1beta1.Coin{}
return protoreflect.ValueOfList(&_SendAuthorization_1_list{list: &list})
case "cosmos.bank.v1beta1.SendAuthorization.allow_list":
list := []string{}
return protoreflect.ValueOfList(&_SendAuthorization_2_list{list: &list})
default:
if fd.IsExtension() {
panic(fmt.Errorf("proto3 declared messages do not support extensions: cosmos.bank.v1beta1.SendAuthorization"))
@ -346,6 +423,12 @@ func (x *fastReflection_SendAuthorization) ProtoMethods() *protoiface.Methods {
n += 1 + l + runtime.Sov(uint64(l))
}
}
if len(x.AllowList) > 0 {
for _, s := range x.AllowList {
l = len(s)
n += 1 + l + runtime.Sov(uint64(l))
}
}
if x.unknownFields != nil {
n += len(x.unknownFields)
}
@ -375,6 +458,15 @@ func (x *fastReflection_SendAuthorization) ProtoMethods() *protoiface.Methods {
i -= len(x.unknownFields)
copy(dAtA[i:], x.unknownFields)
}
if len(x.AllowList) > 0 {
for iNdEx := len(x.AllowList) - 1; iNdEx >= 0; iNdEx-- {
i -= len(x.AllowList[iNdEx])
copy(dAtA[i:], x.AllowList[iNdEx])
i = runtime.EncodeVarint(dAtA, i, uint64(len(x.AllowList[iNdEx])))
i--
dAtA[i] = 0x12
}
}
if len(x.SpendLimit) > 0 {
for iNdEx := len(x.SpendLimit) - 1; iNdEx >= 0; iNdEx-- {
encoded, err := options.Marshal(x.SpendLimit[iNdEx])
@ -474,6 +566,38 @@ func (x *fastReflection_SendAuthorization) ProtoMethods() *protoiface.Methods {
return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, err
}
iNdEx = postIndex
case 2:
if wireType != 2 {
return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field AllowList", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow
}
if iNdEx >= l {
return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength
}
if postIndex > l {
return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF
}
x.AllowList = append(x.AllowList, string(dAtA[iNdEx:postIndex]))
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := runtime.Skip(dAtA[iNdEx:])
@ -532,6 +656,11 @@ type SendAuthorization struct {
unknownFields protoimpl.UnknownFields
SpendLimit []*v1beta1.Coin `protobuf:"bytes,1,rep,name=spend_limit,json=spendLimit,proto3" json:"spend_limit,omitempty"`
// allow_list specifies an optional list of addresses to whom the grantee can send tokens on behalf of the
// granter. If omitted, any recipient is allowed.
//
// Since: cosmos-sdk 0.47
AllowList []string `protobuf:"bytes,2,rep,name=allow_list,json=allowList,proto3" json:"allow_list,omitempty"`
}
func (x *SendAuthorization) Reset() {
@ -561,6 +690,13 @@ func (x *SendAuthorization) GetSpendLimit() []*v1beta1.Coin {
return nil
}
func (x *SendAuthorization) GetAllowList() []string {
if x != nil {
return x.AllowList
}
return nil
}
var File_cosmos_bank_v1beta1_authz_proto protoreflect.FileDescriptor
var file_cosmos_bank_v1beta1_authz_proto_rawDesc = []byte{
@ -572,7 +708,7 @@ var file_cosmos_bank_v1beta1_authz_proto_rawDesc = []byte{
0x73, 0x6d, 0x6f, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x63, 0x6f, 0x73, 0x6d, 0x6f,
0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2f,
0x62, 0x61, 0x73, 0x65, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x63, 0x6f, 0x69,
0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x94, 0x01, 0x0a, 0x11, 0x53, 0x65, 0x6e, 0x64,
0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xb3, 0x01, 0x0a, 0x11, 0x53, 0x65, 0x6e, 0x64,
0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x6c, 0x0a,
0x0b, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x01, 0x20, 0x03,
0x28, 0x0b, 0x32, 0x19, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x62, 0x61, 0x73, 0x65,
@ -580,21 +716,23 @@ var file_cosmos_bank_v1beta1_authz_proto_rawDesc = []byte{
0xde, 0x1f, 0x00, 0xaa, 0xdf, 0x1f, 0x28, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f,
0x6d, 0x2f, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2f, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2d,
0x73, 0x64, 0x6b, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x43, 0x6f, 0x69, 0x6e, 0x73, 0x52,
0x0a, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x3a, 0x11, 0xca, 0xb4, 0x2d,
0x0d, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0xc5,
0x01, 0x0a, 0x17, 0x63, 0x6f, 0x6d, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x62, 0x61,
0x6e, 0x6b, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x42, 0x0a, 0x41, 0x75, 0x74, 0x68,
0x7a, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x30, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73,
0x73, 0x64, 0x6b, 0x2e, 0x69, 0x6f, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x63, 0x6f, 0x73, 0x6d, 0x6f,
0x73, 0x2f, 0x62, 0x61, 0x6e, 0x6b, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x3b, 0x62,
0x61, 0x6e, 0x6b, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0xa2, 0x02, 0x03, 0x43, 0x42, 0x58,
0xaa, 0x02, 0x13, 0x43, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x42, 0x61, 0x6e, 0x6b, 0x2e, 0x56,
0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0xca, 0x02, 0x13, 0x43, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x5c,
0x42, 0x61, 0x6e, 0x6b, 0x5c, 0x56, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0xe2, 0x02, 0x1f, 0x43,
0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x5c, 0x42, 0x61, 0x6e, 0x6b, 0x5c, 0x56, 0x31, 0x62, 0x65, 0x74,
0x61, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02,
0x15, 0x43, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x3a, 0x3a, 0x42, 0x61, 0x6e, 0x6b, 0x3a, 0x3a, 0x56,
0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x0a, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x61,
0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52,
0x09, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x4c, 0x69, 0x73, 0x74, 0x3a, 0x11, 0xca, 0xb4, 0x2d, 0x0d,
0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0xc5, 0x01,
0x0a, 0x17, 0x63, 0x6f, 0x6d, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x62, 0x61, 0x6e,
0x6b, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x42, 0x0a, 0x41, 0x75, 0x74, 0x68, 0x7a,
0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x30, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x73,
0x64, 0x6b, 0x2e, 0x69, 0x6f, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73,
0x2f, 0x62, 0x61, 0x6e, 0x6b, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x3b, 0x62, 0x61,
0x6e, 0x6b, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0xa2, 0x02, 0x03, 0x43, 0x42, 0x58, 0xaa,
0x02, 0x13, 0x43, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x42, 0x61, 0x6e, 0x6b, 0x2e, 0x56, 0x31,
0x62, 0x65, 0x74, 0x61, 0x31, 0xca, 0x02, 0x13, 0x43, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x5c, 0x42,
0x61, 0x6e, 0x6b, 0x5c, 0x56, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0xe2, 0x02, 0x1f, 0x43, 0x6f,
0x73, 0x6d, 0x6f, 0x73, 0x5c, 0x42, 0x61, 0x6e, 0x6b, 0x5c, 0x56, 0x31, 0x62, 0x65, 0x74, 0x61,
0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x15,
0x43, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x3a, 0x3a, 0x42, 0x61, 0x6e, 0x6b, 0x3a, 0x3a, 0x56, 0x31,
0x62, 0x65, 0x74, 0x61, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (

View File

@ -16,4 +16,10 @@ message SendAuthorization {
repeated cosmos.base.v1beta1.Coin spend_limit = 1
[(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"];
// allow_list specifies an optional list of addresses to whom the grantee can send tokens on behalf of the
// granter. If omitted, any recipient is allowed.
//
// Since: cosmos-sdk 0.47
repeated string allow_list = 2;
}

View File

@ -26,6 +26,7 @@ const (
FlagExpiration = "expiration"
FlagAllowedValidators = "allowed-validators"
FlagDenyValidators = "deny-validators"
FlagAllowList = "allow-list"
delegate = "delegate"
redelegate = "redelegate"
unbond = "unbond"
@ -93,7 +94,18 @@ Examples:
return fmt.Errorf("spend-limit should be greater than zero")
}
authorization = bank.NewSendAuthorization(spendLimit)
allowList, err := cmd.Flags().GetStringSlice(FlagAllowList)
if err != nil {
return err
}
allowed, err := bech32toAccAddresses(allowList)
if err != nil {
return err
}
authorization = bank.NewSendAuthorization(spendLimit, allowed)
case "generic":
msgType, err := cmd.Flags().GetString(FlagMsgType)
if err != nil {
@ -140,12 +152,12 @@ Examples:
delegateLimit = &spendLimit
}
allowed, err := bech32toValidatorAddresses(allowValidators)
allowed, err := bech32toValAddresses(allowValidators)
if err != nil {
return err
}
denied, err := bech32toValidatorAddresses(denyValidators)
denied, err := bech32toValAddresses(denyValidators)
if err != nil {
return err
}
@ -184,6 +196,7 @@ Examples:
cmd.Flags().String(FlagSpendLimit, "", "SpendLimit for Send Authorization, an array of Coins allowed spend")
cmd.Flags().StringSlice(FlagAllowedValidators, []string{}, "Allowed validators addresses separated by ,")
cmd.Flags().StringSlice(FlagDenyValidators, []string{}, "Deny validators addresses separated by ,")
cmd.Flags().StringSlice(FlagAllowList, []string{}, "Allowed addresses grantee is allowed to send funds separated by ,")
cmd.Flags().Int64(FlagExpiration, 0, "Expire time as Unix timestamp. Set zero (0) for no expiry. Default is 0.")
return cmd
}
@ -273,7 +286,8 @@ Example:
return cmd
}
func bech32toValidatorAddresses(validators []string) ([]sdk.ValAddress, error) {
// bech32toValAddresses returns []ValAddress from a list of Bech32 string addresses.
func bech32toValAddresses(validators []string) ([]sdk.ValAddress, error) {
vals := make([]sdk.ValAddress, len(validators))
for i, validator := range validators {
addr, err := sdk.ValAddressFromBech32(validator)
@ -284,3 +298,16 @@ func bech32toValidatorAddresses(validators []string) ([]sdk.ValAddress, error) {
}
return vals, nil
}
// bech32toAccAddresses returns []AccAddress from a list of Bech32 string addresses.
func bech32toAccAddresses(accAddrs []string) ([]sdk.AccAddress, error) {
addrs := make([]sdk.AccAddress, len(accAddrs))
for i, addr := range accAddrs {
accAddr, err := sdk.AccAddressFromBech32(addr)
if err != nil {
return nil, err
}
addrs[i] = accAddr
}
return addrs, nil
}

View File

@ -195,7 +195,7 @@ func (s *IntegrationTestSuite) TestQueryGranterGrantsGRPC() {
fmt.Sprintf("%s/cosmos/authz/v1beta1/grants/granter/%s", val.APIAddress, val.Address.String()),
false,
"",
7,
8,
},
}
for _, tc := range testCases {

View File

@ -161,7 +161,18 @@ func (s *IntegrationTestSuite) TestQueryAuthorization() {
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
false,
`{"@type":"/cosmos.bank.v1beta1.SendAuthorization","spend_limit":[{"denom":"stake","amount":"100"}]}`,
`{"@type":"/cosmos.bank.v1beta1.SendAuthorization","spend_limit":[{"denom":"stake","amount":"100"}],"allow_list":[]}`,
},
{
"Valid txn with allowed list (json)",
[]string{
val.Address.String(),
s.grantee[3].String(),
typeMsgSend,
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
false,
fmt.Sprintf(`{"@type":"/cosmos.bank.v1beta1.SendAuthorization","spend_limit":[{"denom":"stake","amount":"88"}],"allow_list":["%s"]}`, s.grantee[4]),
},
}
for _, tc := range testCases {
@ -221,7 +232,7 @@ func (s *IntegrationTestSuite) TestQueryGranterGrants() {
},
false,
"",
7,
8,
},
{
"valid case with pagination",

View File

@ -45,7 +45,7 @@ func (s *IntegrationTestSuite) SetupSuite() {
s.Require().NoError(err)
val := s.network.Validators[0]
s.grantee = make([]sdk.AccAddress, 3)
s.grantee = make([]sdk.AccAddress, 6)
// Send some funds to the new account.
// Create new account in the keyring.
@ -86,7 +86,7 @@ func (s *IntegrationTestSuite) SetupSuite() {
s.grantee[2] = s.createAccount("grantee3")
// grant send authorization to grantee3
out, err = CreateGrant(val, []string{
_, err = CreateGrant(val, []string{
s.grantee[2].String(),
"send",
fmt.Sprintf("--%s=100stake", cli.FlagSpendLimit),
@ -98,6 +98,30 @@ func (s *IntegrationTestSuite) SetupSuite() {
})
s.Require().NoError(err)
// Create new accounts in the keyring.
s.grantee[3] = s.createAccount("grantee4")
s.msgSendExec(s.grantee[3])
s.grantee[4] = s.createAccount("grantee5")
s.grantee[5] = s.createAccount("grantee6")
// grant send authorization with allow list to grantee4
out, err = CreateGrant(
val,
[]string{
s.grantee[3].String(),
"send",
fmt.Sprintf("--%s=100stake", cli.FlagSpendLimit),
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()),
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock),
fmt.Sprintf("--%s=%d", cli.FlagExpiration, time.Now().Add(time.Minute*time.Duration(120)).Unix()),
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()),
fmt.Sprintf("--%s=%s", cli.FlagAllowList, s.grantee[4]),
},
)
s.Require().NoError(err)
err = s.network.WaitForNextBlock()
s.Require().NoError(err)
@ -404,6 +428,40 @@ func (s *IntegrationTestSuite) TestCLITxGrantAuthorization() {
false,
"",
},
{
"Valid tx send authorization with allow list",
[]string{
grantee.String(),
"send",
fmt.Sprintf("--%s=100stake", cli.FlagSpendLimit),
fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()),
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock),
fmt.Sprintf("--%s=%d", cli.FlagExpiration, twoHours),
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()),
fmt.Sprintf("--%s=%s", cli.FlagAllowList, s.grantee[1]),
},
0,
false,
"",
},
{
"Invalid tx send authorization with duplicate allow list",
[]string{
grantee.String(),
"send",
fmt.Sprintf("--%s=100stake", cli.FlagSpendLimit),
fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()),
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock),
fmt.Sprintf("--%s=%d", cli.FlagExpiration, twoHours),
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()),
fmt.Sprintf("--%s=%s", cli.FlagAllowList, fmt.Sprintf("%s,%s", s.grantee[1], s.grantee[1])),
},
0,
true,
"duplicate entry",
},
{
"Valid tx generic authorization",
[]string{
@ -877,6 +935,86 @@ func (s *IntegrationTestSuite) TestNewExecGrantAuthorized() {
}
}
func (s *IntegrationTestSuite) TestExecSendAuthzWithAllowList() {
val := s.network.Validators[0]
grantee := s.grantee[3]
allowedAddr := s.grantee[4]
notAllowedAddr := s.grantee[5]
twoHours := time.Now().Add(time.Minute * time.Duration(120)).Unix()
_, err := CreateGrant(
val,
[]string{
grantee.String(),
"send",
fmt.Sprintf("--%s=100stake", cli.FlagSpendLimit),
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()),
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock),
fmt.Sprintf("--%s=%d", cli.FlagExpiration, twoHours),
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()),
fmt.Sprintf("--%s=%s", cli.FlagAllowList, allowedAddr),
},
)
s.Require().NoError(err)
tokens := sdk.NewCoins(
sdk.NewCoin("stake", sdk.NewInt(12)),
)
validGeneratedTx, err := banktestutil.MsgSendExec(
val.ClientCtx,
val.Address,
allowedAddr,
tokens,
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock),
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()),
fmt.Sprintf("--%s=true", flags.FlagGenerateOnly),
)
s.Require().NoError(err)
execMsg := testutil.WriteToNewTempFile(s.T(), validGeneratedTx.String())
invalidGeneratedTx, err := banktestutil.MsgSendExec(
val.ClientCtx,
val.Address,
notAllowedAddr,
tokens,
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock),
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()),
fmt.Sprintf("--%s=true", flags.FlagGenerateOnly),
)
s.Require().NoError(err)
execMsg1 := testutil.WriteToNewTempFile(s.T(), invalidGeneratedTx.String())
// test sending to allowed address
args := []string{
execMsg.Name(),
fmt.Sprintf("--%s=%s", flags.FlagFrom, grantee.String()),
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock),
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()),
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
}
var response sdk.TxResponse
cmd := cli.NewCmdExecAuthorization()
out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, cmd, args)
s.Require().NoError(err)
s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &response), out.String())
// test sending to not allowed address
args = []string{
execMsg1.Name(),
fmt.Sprintf("--%s=%s", flags.FlagFrom, grantee.String()),
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock),
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()),
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
}
out, err = clitestutil.ExecTestCLICmd(val.ClientCtx, cmd, args)
s.Require().NoError(err)
s.Contains(out.String(), fmt.Sprintf("cannot send to %s address", notAllowedAddr))
}
func (s *IntegrationTestSuite) TestExecDelegateAuthorization() {
val := s.network.Validators[0]
grantee := s.grantee[0]

View File

@ -125,9 +125,10 @@ func (s *TestSuite) TestKeeperIter() {
granteeAddr := addrs[1]
granter2Addr := addrs[2]
e := ctx.BlockTime().AddDate(1, 0, 0)
sendAuthz := banktypes.NewSendAuthorization(coins100, nil)
s.authzKeeper.SaveGrant(ctx, granteeAddr, granterAddr, banktypes.NewSendAuthorization(coins100), &e)
s.authzKeeper.SaveGrant(ctx, granteeAddr, granter2Addr, banktypes.NewSendAuthorization(coins100), &e)
s.authzKeeper.SaveGrant(ctx, granteeAddr, granterAddr, sendAuthz, &e)
s.authzKeeper.SaveGrant(ctx, granteeAddr, granter2Addr, sendAuthz, &e)
s.authzKeeper.IterateGrants(ctx, func(granter, grantee sdk.AccAddress, grant authz.Grant) bool {
s.Require().Equal(granteeAddr, grantee)
@ -144,7 +145,7 @@ func (s *TestSuite) TestDispatchAction() {
granterAddr := addrs[0]
granteeAddr := addrs[1]
recipientAddr := addrs[2]
a := banktypes.NewSendAuthorization(coins100)
a := banktypes.NewSendAuthorization(coins100, nil)
require.NoError(banktestutil.FundAccount(s.bankKeeper, s.ctx, granterAddr, coins1000))

View File

@ -35,6 +35,7 @@ func TestMigration(t *testing.T) {
blockTime := ctx.BlockTime()
oneDay := blockTime.AddDate(0, 0, 1)
oneYear := blockTime.AddDate(1, 0, 0)
sendAuthz := banktypes.NewSendAuthorization(coins100, nil)
grants := []struct {
granter sdk.AccAddress
@ -47,7 +48,7 @@ func TestMigration(t *testing.T) {
grantee1,
sendMsgType,
func() authz.Grant {
any, err := codectypes.NewAnyWithValue(banktypes.NewSendAuthorization(coins100))
any, err := codectypes.NewAnyWithValue(sendAuthz)
require.NoError(t, err)
return authz.Grant{
Authorization: any,
@ -60,7 +61,7 @@ func TestMigration(t *testing.T) {
grantee2,
sendMsgType,
func() authz.Grant {
any, err := codectypes.NewAnyWithValue(banktypes.NewSendAuthorization(coins100))
any, err := codectypes.NewAnyWithValue(sendAuthz)
require.NoError(t, err)
return authz.Grant{
Authorization: any,

View File

@ -44,9 +44,10 @@ func TestExpiredGrantsQueue(t *testing.T) {
expiration := ctx.BlockTime().AddDate(0, 1, 0)
expiration2 := expiration.AddDate(1, 0, 0)
smallCoins := sdk.NewCoins(sdk.NewInt64Coin("stake", 10))
sendAuthz := banktypes.NewSendAuthorization(smallCoins, nil)
save := func(grantee sdk.AccAddress, exp *time.Time) {
err := authzKeeper.SaveGrant(ctx, grantee, granter, banktypes.NewSendAuthorization(smallCoins), exp)
err := authzKeeper.SaveGrant(ctx, grantee, granter, sendAuthz, exp)
require.NoError(t, err, "Grant from %s", grantee.String())
}
save(grantee1, &expiration)

View File

@ -174,7 +174,8 @@ func TestAminoJSON(t *testing.T) {
require.NoError(t, err)
grant, err := authz.NewGrant(blockTime, authz.NewGenericAuthorization(typeURL), &expiresAt)
require.NoError(t, err)
sendGrant, err := authz.NewGrant(blockTime, banktypes.NewSendAuthorization(sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(1000)))), &expiresAt)
sendAuthz := banktypes.NewSendAuthorization(sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(1000))), nil)
sendGrant, err := authz.NewGrant(blockTime, sendAuthz, &expiresAt)
require.NoError(t, err)
valAddr, err := sdk.ValAddressFromBech32("cosmosvaloper1xcy3els9ua75kdm783c3qu0rfa2eples6eavqq")
require.NoError(t, err)

View File

@ -26,7 +26,8 @@ func TestDecodeStore(t *testing.T) {
now := time.Now().UTC()
e := now.Add(1)
grant, _ := authz.NewGrant(now, banktypes.NewSendAuthorization(sdk.NewCoins(sdk.NewInt64Coin("foo", 123))), &e)
sendAuthz := banktypes.NewSendAuthorization(sdk.NewCoins(sdk.NewInt64Coin("foo", 123)), nil)
grant, _ := authz.NewGrant(now, sendAuthz, &e)
grantBz, err := cdc.Marshal(&grant)
require.NoError(t, err)
kvPairs := kv.Pairs{

View File

@ -37,7 +37,8 @@ func genGrant(r *rand.Rand, accounts []simtypes.Account, genT time.Time) []authz
func generateRandomGrant(r *rand.Rand) *codectypes.Any {
authorizations := make([]*codectypes.Any, 2)
authorizations[0] = newAnyAuthorization(banktypes.NewSendAuthorization(sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(1000)))))
sendAuthz := banktypes.NewSendAuthorization(sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(1000))), nil)
authorizations[0] = newAnyAuthorization(sendAuthz)
authorizations[1] = newAnyAuthorization(authz.NewGenericAuthorization(sdk.MsgTypeURL(&v1.MsgSubmitProposal{})))
return authorizations[r.Intn(len(authorizations))]
@ -57,7 +58,9 @@ func RandomizedGenState(simState *module.SimulationState) {
var grants []authz.GrantAuthorization
simState.AppParams.GetOrGenerate(
simState.Cdc, "authz", &grants, simState.Rand,
func(r *rand.Rand) { grants = genGrant(r, simState.Accounts, simState.GenTimestamp) },
func(r *rand.Rand) {
grants = genGrant(r, simState.Accounts, simState.GenTimestamp)
},
)
authzGrantsGenesis := authz.NewGenesisState(grants)

View File

@ -118,7 +118,9 @@ func SimulateMsgGrant(cdc *codec.ProtoCodec, ak authz.AccountKeeper, bk authz.Ba
if !t1.Before(ctx.BlockTime()) {
expiration = &t1
}
msg, err := authz.NewMsgGrant(granter.Address, grantee.Address, generateRandomAuthorization(r, spendLimit), expiration)
randomAuthz := generateRandomAuthorization(r, spendLimit)
msg, err := authz.NewMsgGrant(granter.Address, grantee.Address, randomAuthz, expiration)
if err != nil {
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgGrant, err.Error()), nil, err
}
@ -148,7 +150,8 @@ func SimulateMsgGrant(cdc *codec.ProtoCodec, ak authz.AccountKeeper, bk authz.Ba
func generateRandomAuthorization(r *rand.Rand, spendLimit sdk.Coins) authz.Authorization {
authorizations := make([]authz.Authorization, 2)
authorizations[0] = banktype.NewSendAuthorization(spendLimit)
sendAuthz := banktype.NewSendAuthorization(spendLimit, nil)
authorizations[0] = sendAuthz
authorizations[1] = authz.NewGenericAuthorization(sdk.MsgTypeURL(&banktype.MsgSend{}))
return authorizations[r.Intn(len(authorizations))]

View File

@ -161,7 +161,7 @@ func (suite *SimTestSuite) TestSimulateRevoke() {
granter := accounts[0]
grantee := accounts[1]
a := banktypes.NewSendAuthorization(initCoins)
a := banktypes.NewSendAuthorization(initCoins, nil)
expire := time.Now().Add(30 * time.Hour)
err := suite.authzKeeper.SaveGrant(suite.ctx, grantee.Address, granter.Address, a, &expire)
@ -196,7 +196,7 @@ func (suite *SimTestSuite) TestSimulateExec() {
granter := accounts[0]
grantee := accounts[1]
a := banktypes.NewSendAuthorization(initCoins)
a := banktypes.NewSendAuthorization(initCoins, nil)
expire := suite.ctx.BlockTime().Add(1 * time.Hour)
err := suite.authzKeeper.SaveGrant(suite.ctx, grantee.Address, granter.Address, a, &expire)

View File

@ -32,6 +32,11 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
// Since: cosmos-sdk 0.43
type SendAuthorization struct {
SpendLimit github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,1,rep,name=spend_limit,json=spendLimit,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"spend_limit"`
// allow_list specifies an optional list of addresses to whom the grantee can send tokens on behalf of the
// granter. If omitted, any recipient is allowed.
//
// Since: cosmos-sdk 0.47
AllowList []string `protobuf:"bytes,2,rep,name=allow_list,json=allowList,proto3" json:"allow_list,omitempty"`
}
func (m *SendAuthorization) Reset() { *m = SendAuthorization{} }
@ -74,6 +79,13 @@ func (m *SendAuthorization) GetSpendLimit() github_com_cosmos_cosmos_sdk_types.C
return nil
}
func (m *SendAuthorization) GetAllowList() []string {
if m != nil {
return m.AllowList
}
return nil
}
func init() {
proto.RegisterType((*SendAuthorization)(nil), "cosmos.bank.v1beta1.SendAuthorization")
}
@ -81,24 +93,25 @@ func init() {
func init() { proto.RegisterFile("cosmos/bank/v1beta1/authz.proto", fileDescriptor_a4d2a37888ea779f) }
var fileDescriptor_a4d2a37888ea779f = []byte{
// 257 bytes of a gzipped FileDescriptorProto
// 285 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4f, 0xce, 0x2f, 0xce,
0xcd, 0x2f, 0xd6, 0x4f, 0x4a, 0xcc, 0xcb, 0xd6, 0x2f, 0x33, 0x4c, 0x4a, 0x2d, 0x49, 0x34, 0xd4,
0x4f, 0x2c, 0x2d, 0xc9, 0xa8, 0xd2, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x86, 0x28, 0xd0,
0x03, 0x29, 0xd0, 0x83, 0x2a, 0x90, 0x12, 0x49, 0xcf, 0x4f, 0xcf, 0x07, 0xcb, 0xeb, 0x83, 0x58,
0x10, 0xa5, 0x52, 0x92, 0x10, 0xa5, 0xf1, 0x10, 0x09, 0xa8, 0x3e, 0x88, 0x94, 0x1c, 0xdc, 0x9a,
0xe2, 0x54, 0xb8, 0x35, 0xc9, 0xf9, 0x99, 0x79, 0x10, 0x79, 0xa5, 0x29, 0x8c, 0x5c, 0x82, 0xc1,
0xe2, 0x54, 0xb8, 0x35, 0xc9, 0xf9, 0x99, 0x79, 0x10, 0x79, 0xa5, 0xcd, 0x8c, 0x5c, 0x82, 0xc1,
0xa9, 0x79, 0x29, 0x8e, 0xa5, 0x25, 0x19, 0xf9, 0x45, 0x99, 0x55, 0x89, 0x25, 0x99, 0xf9, 0x79,
0x42, 0x39, 0x5c, 0xdc, 0xc5, 0x05, 0xa9, 0x79, 0x29, 0xf1, 0x39, 0x99, 0xb9, 0x99, 0x25, 0x12,
0x8c, 0x0a, 0xcc, 0x1a, 0xdc, 0x46, 0x92, 0x7a, 0x70, 0x17, 0x15, 0xa7, 0xc2, 0x5c, 0xa4, 0xe7,
0x9c, 0x9f, 0x99, 0xe7, 0x64, 0x70, 0xe2, 0x9e, 0x3c, 0xc3, 0xaa, 0xfb, 0xf2, 0x1a, 0xe9, 0x99,
0x25, 0x19, 0xa5, 0x49, 0x7a, 0xc9, 0xf9, 0xb9, 0x50, 0x67, 0x40, 0x29, 0xdd, 0xe2, 0x94, 0x6c,
0xfd, 0x92, 0xca, 0x82, 0xd4, 0x62, 0xb0, 0x86, 0xe2, 0x20, 0x2e, 0xb0, 0xf9, 0x3e, 0x20, 0xe3,
0xad, 0x04, 0x4f, 0x6d, 0xd1, 0xe5, 0x45, 0x71, 0x80, 0x93, 0xf3, 0x89, 0x47, 0x72, 0x8c, 0x17,
0x1e, 0xc9, 0x31, 0x3e, 0x78, 0x24, 0xc7, 0x38, 0xe1, 0xb1, 0x1c, 0xc3, 0x85, 0xc7, 0x72, 0x0c,
0x37, 0x1e, 0xcb, 0x31, 0x44, 0x69, 0xe2, 0xb5, 0xa2, 0x02, 0x12, 0x9e, 0x60, 0x9b, 0x92, 0xd8,
0xc0, 0x5e, 0x34, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0x8c, 0x86, 0xc3, 0x2e, 0x6b, 0x01, 0x00,
0x00,
0x85, 0x64, 0xb9, 0xb8, 0x12, 0x73, 0x72, 0xf2, 0xcb, 0xe3, 0x73, 0x32, 0x8b, 0x4b, 0x24, 0x98,
0x14, 0x98, 0x35, 0x38, 0x83, 0x38, 0xc1, 0x22, 0x3e, 0x99, 0xc5, 0x25, 0x56, 0x82, 0xa7, 0xb6,
0xe8, 0xf2, 0xa2, 0xb8, 0xcf, 0xc9, 0xf9, 0xc4, 0x23, 0x39, 0xc6, 0x0b, 0x8f, 0xe4, 0x18, 0x1f,
0x3c, 0x92, 0x63, 0x9c, 0xf0, 0x58, 0x8e, 0xe1, 0xc2, 0x63, 0x39, 0x86, 0x1b, 0x8f, 0xe5, 0x18,
0xa2, 0x34, 0xf1, 0xba, 0xa0, 0x02, 0x12, 0xdc, 0x60, 0x87, 0x24, 0xb1, 0x81, 0x43, 0xc0, 0x18,
0x10, 0x00, 0x00, 0xff, 0xff, 0xbd, 0x94, 0xb9, 0xfa, 0x8a, 0x01, 0x00, 0x00,
}
func (m *SendAuthorization) Marshal() (dAtA []byte, err error) {
@ -121,6 +134,15 @@ func (m *SendAuthorization) MarshalToSizedBuffer(dAtA []byte) (int, error) {
_ = i
var l int
_ = l
if len(m.AllowList) > 0 {
for iNdEx := len(m.AllowList) - 1; iNdEx >= 0; iNdEx-- {
i -= len(m.AllowList[iNdEx])
copy(dAtA[i:], m.AllowList[iNdEx])
i = encodeVarintAuthz(dAtA, i, uint64(len(m.AllowList[iNdEx])))
i--
dAtA[i] = 0x12
}
}
if len(m.SpendLimit) > 0 {
for iNdEx := len(m.SpendLimit) - 1; iNdEx >= 0; iNdEx-- {
{
@ -161,6 +183,12 @@ func (m *SendAuthorization) Size() (n int) {
n += 1 + l + sovAuthz(uint64(l))
}
}
if len(m.AllowList) > 0 {
for _, s := range m.AllowList {
l = len(s)
n += 1 + l + sovAuthz(uint64(l))
}
}
return n
}
@ -233,6 +261,38 @@ func (m *SendAuthorization) Unmarshal(dAtA []byte) error {
return err
}
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field AllowList", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowAuthz
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthAuthz
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthAuthz
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.AllowList = append(m.AllowList, string(dAtA[iNdEx:postIndex]))
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipAuthz(dAtA[iNdEx:])

View File

@ -12,5 +12,6 @@ var (
ErrSendDisabled = sdkerrors.Register(ModuleName, 5, "send transactions are disabled")
ErrDenomMetadataNotFound = sdkerrors.Register(ModuleName, 6, "client denom metadata not found")
ErrInvalidKey = sdkerrors.Register(ModuleName, 7, "invalid key")
ErrMultipleSenders = sdkerrors.Register(ModuleName, 8, "multiple senders not allowed")
ErrDuplicateEntry = sdkerrors.Register(ModuleName, 8, "duplicate entry")
ErrMultipleSenders = sdkerrors.Register(ModuleName, 9, "multiple senders not allowed")
)

View File

@ -6,13 +6,21 @@ import (
"github.com/cosmos/cosmos-sdk/x/authz"
)
// TODO: Revisit this once we have propoer gas fee framework.
// Tracking issues https://github.com/cosmos/cosmos-sdk/issues/9054, https://github.com/cosmos/cosmos-sdk/discussions/9072
const gasCostPerIteration = uint64(10)
var _ authz.Authorization = &SendAuthorization{}
// NewSendAuthorization creates a new SendAuthorization object.
func NewSendAuthorization(spendLimit sdk.Coins) *SendAuthorization {
return &SendAuthorization{
SpendLimit: spendLimit,
}
func NewSendAuthorization(spendLimit sdk.Coins, allowed []sdk.AccAddress) *SendAuthorization {
allowedAddrs := toBech32Addresses(allowed)
a := SendAuthorization{}
a.AllowList = allowedAddrs
a.SpendLimit = spendLimit
return &a
}
// MsgTypeURL implements Authorization.MsgTypeURL.
@ -26,6 +34,7 @@ func (a SendAuthorization) Accept(ctx sdk.Context, msg sdk.Msg) (authz.AcceptRes
if !ok {
return authz.AcceptResponse{}, sdkerrors.ErrInvalidType.Wrap("type mismatch")
}
toAddr := mSend.ToAddress
limitLeft, isNegative := a.SpendLimit.SafeSub(mSend.Amount...)
if isNegative {
return authz.AcceptResponse{}, sdkerrors.ErrInsufficientFunds.Wrapf("requested amount is more than spend limit")
@ -34,7 +43,22 @@ func (a SendAuthorization) Accept(ctx sdk.Context, msg sdk.Msg) (authz.AcceptRes
return authz.AcceptResponse{Accept: true, Delete: true}, nil
}
return authz.AcceptResponse{Accept: true, Delete: false, Updated: &SendAuthorization{SpendLimit: limitLeft}}, nil
isAddrExists := false
allowedList := a.GetAllowList()
for _, addr := range allowedList {
ctx.GasMeter().ConsumeGas(gasCostPerIteration, "send authorization")
if addr == toAddr {
isAddrExists = true
break
}
}
if len(allowedList) > 0 && !isAddrExists {
return authz.AcceptResponse{}, sdkerrors.ErrUnauthorized.Wrapf("cannot send to %s address", toAddr)
}
return authz.AcceptResponse{Accept: true, Delete: false, Updated: &SendAuthorization{SpendLimit: limitLeft, AllowList: allowedList}}, nil
}
// ValidateBasic implements Authorization.ValidateBasic.
@ -45,5 +69,25 @@ func (a SendAuthorization) ValidateBasic() error {
if !a.SpendLimit.IsAllPositive() {
return sdkerrors.ErrInvalidCoins.Wrapf("spend limit must be positive")
}
found := make(map[string]bool, 0)
for i := 0; i < len(a.AllowList); i++ {
if found[a.AllowList[i]] {
return ErrDuplicateEntry
}
found[a.AllowList[i]] = true
}
return nil
}
func toBech32Addresses(allowed []sdk.AccAddress) []string {
if len(allowed) == 0 {
return nil
}
allowedAddrs := make([]string, len(allowed))
for i, addr := range allowed {
allowedAddrs[i] = addr.String()
}
return allowedAddrs
}

View File

@ -1,6 +1,7 @@
package types_test
import (
fmt "fmt"
"testing"
"github.com/stretchr/testify/require"
@ -12,16 +13,19 @@ import (
)
var (
coins1000 = sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(1000)))
coins500 = sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(500)))
fromAddr = sdk.AccAddress("_____from _____")
toAddr = sdk.AccAddress("_______to________")
coins1000 = sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(1000)))
coins500 = sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(500)))
fromAddr = sdk.AccAddress("_____from _____")
toAddr = sdk.AccAddress("_______to________")
unknownAddr = sdk.AccAddress("_____unknown_____")
)
func TestSendAuthorization(t *testing.T) {
app := simapp.Setup(t, false)
ctx := app.BaseApp.NewContext(false, tmproto.Header{})
authorization := types.NewSendAuthorization(coins1000)
allowList := make([]sdk.AccAddress, 1)
allowList[0] = toAddr
authorization := types.NewSendAuthorization(coins1000, nil)
t.Log("verify authorization returns valid method name")
require.Equal(t, authorization.MsgTypeURL(), "/cosmos.bank.v1beta1.MsgSend")
@ -36,7 +40,7 @@ func TestSendAuthorization(t *testing.T) {
require.True(t, resp.Delete)
require.Nil(t, resp.Updated)
authorization = types.NewSendAuthorization(coins1000)
authorization = types.NewSendAuthorization(coins1000, nil)
require.Equal(t, authorization.MsgTypeURL(), "/cosmos.bank.v1beta1.MsgSend")
require.NoError(t, authorization.ValidateBasic())
send = types.NewMsgSend(fromAddr, toAddr, coins500)
@ -47,7 +51,7 @@ func TestSendAuthorization(t *testing.T) {
require.NoError(t, err)
require.False(t, resp.Delete)
require.NotNil(t, resp.Updated)
sendAuth := types.NewSendAuthorization(coins500)
sendAuth := types.NewSendAuthorization(coins500, nil)
require.Equal(t, sendAuth.String(), resp.Updated.String())
t.Log("expect updated authorization nil after spending remaining amount")
@ -55,4 +59,17 @@ func TestSendAuthorization(t *testing.T) {
require.NoError(t, err)
require.True(t, resp.Delete)
require.Nil(t, resp.Updated)
t.Log("allow list and no address")
authzWithAllowList := types.NewSendAuthorization(coins1000, allowList)
require.Equal(t, authzWithAllowList.MsgTypeURL(), "/cosmos.bank.v1beta1.MsgSend")
require.NoError(t, authorization.ValidateBasic())
send = types.NewMsgSend(fromAddr, unknownAddr, coins500)
require.NoError(t, authzWithAllowList.ValidateBasic())
resp, err = authzWithAllowList.Accept(ctx, send)
require.False(t, resp.Accept)
require.False(t, resp.Delete)
require.Nil(t, resp.Updated)
require.Error(t, err)
require.Contains(t, err.Error(), fmt.Sprintf("cannot send to %s address", unknownAddr))
}