github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/smartcontract/manifest/permission.go (about) 1 package manifest 2 3 import ( 4 "crypto/elliptic" 5 "encoding/json" 6 "errors" 7 "fmt" 8 9 "github.com/nspcc-dev/neo-go/pkg/crypto/keys" 10 "github.com/nspcc-dev/neo-go/pkg/util" 11 "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" 12 ) 13 14 // PermissionType represents permission type. 15 type PermissionType uint8 16 17 const ( 18 // PermissionWildcard allows everything. 19 PermissionWildcard PermissionType = 0 20 // PermissionHash restricts called contracts based on hash. 21 PermissionHash PermissionType = 1 22 // PermissionGroup restricts called contracts based on public key. 23 PermissionGroup PermissionType = 2 24 ) 25 26 // PermissionDesc is a permission descriptor. 27 type PermissionDesc struct { 28 Type PermissionType 29 Value any 30 } 31 32 // Permission describes which contracts may be invoked and which methods are called. 33 type Permission struct { 34 Contract PermissionDesc `json:"contract"` 35 Methods WildStrings `json:"methods"` 36 } 37 38 // Permissions is just an array of Permission. 39 type Permissions []Permission 40 41 type permissionAux struct { 42 Contract PermissionDesc `json:"contract"` 43 Methods WildStrings `json:"methods"` 44 } 45 46 // NewPermission returns a new permission of the given type. 47 func NewPermission(typ PermissionType, args ...any) *Permission { 48 return &Permission{ 49 Contract: *newPermissionDesc(typ, args...), 50 } 51 } 52 53 func newPermissionDesc(typ PermissionType, args ...any) *PermissionDesc { 54 desc := &PermissionDesc{Type: typ} 55 switch typ { 56 case PermissionWildcard: 57 if len(args) != 0 { 58 panic("wildcard permission has no arguments") 59 } 60 case PermissionHash: 61 if len(args) == 0 { 62 panic("hash permission should have an argument") 63 } else if u, ok := args[0].(util.Uint160); !ok { 64 panic("hash permission should have util.Uint160 argument") 65 } else { 66 desc.Value = u 67 } 68 case PermissionGroup: 69 if len(args) == 0 { 70 panic("group permission should have an argument") 71 } else if pub, ok := args[0].(*keys.PublicKey); !ok { 72 panic("group permission should have a public key argument") 73 } else { 74 desc.Value = pub 75 } 76 } 77 return desc 78 } 79 80 // Hash returns hash for hash-permission. 81 func (d *PermissionDesc) Hash() util.Uint160 { 82 return d.Value.(util.Uint160) 83 } 84 85 // Group returns group's public key for group-permission. 86 func (d *PermissionDesc) Group() *keys.PublicKey { 87 return d.Value.(*keys.PublicKey) 88 } 89 90 // Less returns true if this value is less than the given PermissionDesc value. 91 func (d *PermissionDesc) Less(d1 PermissionDesc) bool { 92 if d.Type < d1.Type { 93 return true 94 } 95 if d.Type != d1.Type { 96 return false 97 } 98 switch d.Type { 99 case PermissionHash: 100 return d.Hash().Less(d1.Hash()) 101 case PermissionGroup: 102 return d.Group().Cmp(d1.Group()) < 0 103 } 104 return false 105 } 106 107 // Equals returns true if both PermissionDesc values are the same. 108 func (d *PermissionDesc) Equals(v PermissionDesc) bool { 109 if d.Type != v.Type { 110 return false 111 } 112 switch d.Type { 113 case PermissionHash: 114 return d.Hash().Equals(v.Hash()) 115 case PermissionGroup: 116 return d.Group().Cmp(v.Group()) == 0 117 } 118 return false 119 } 120 121 // IsValid checks if Permission is correct. 122 func (p *Permission) IsValid() error { 123 for i := range p.Methods.Value { 124 if p.Methods.Value[i] == "" { 125 return errors.New("empty method name") 126 } 127 } 128 if len(p.Methods.Value) < 2 { 129 return nil 130 } 131 names := make([]string, len(p.Methods.Value)) 132 copy(names, p.Methods.Value) 133 if stringsHaveDups(names) { 134 return errors.New("duplicate method names") 135 } 136 return nil 137 } 138 139 // AreValid checks each Permission and ensures there are no duplicates. 140 func (ps Permissions) AreValid() error { 141 for i := range ps { 142 err := ps[i].IsValid() 143 if err != nil { 144 return err 145 } 146 } 147 if len(ps) < 2 { 148 return nil 149 } 150 contracts := make([]PermissionDesc, 0, len(ps)) 151 for i := range ps { 152 contracts = append(contracts, ps[i].Contract) 153 } 154 if permissionDescsHaveDups(contracts) { 155 return errors.New("contracts have duplicates") 156 } 157 return nil 158 } 159 160 // IsAllowed checks if the method is allowed to be executed. 161 func (p *Permission) IsAllowed(hash util.Uint160, m *Manifest, method string) bool { 162 switch p.Contract.Type { 163 case PermissionWildcard: 164 case PermissionHash: 165 if !p.Contract.Hash().Equals(hash) { 166 return false 167 } 168 case PermissionGroup: 169 has := false 170 g := p.Contract.Group() 171 for i := range m.Groups { 172 if g.Equal(m.Groups[i].PublicKey) { 173 has = true 174 break 175 } 176 } 177 if !has { 178 return false 179 } 180 default: 181 panic(fmt.Sprintf("unexpected permission: %d", p.Contract.Type)) 182 } 183 if p.Methods.IsWildcard() { 184 return true 185 } 186 return p.Methods.Contains(method) 187 } 188 189 // UnmarshalJSON implements the json.Unmarshaler interface. 190 func (p *Permission) UnmarshalJSON(data []byte) error { 191 aux := new(permissionAux) 192 if err := json.Unmarshal(data, aux); err != nil { 193 return err 194 } 195 p.Contract = aux.Contract 196 p.Methods = aux.Methods 197 return nil 198 } 199 200 // MarshalJSON implements the json.Marshaler interface. 201 func (d *PermissionDesc) MarshalJSON() ([]byte, error) { 202 switch d.Type { 203 case PermissionHash: 204 return json.Marshal("0x" + d.Hash().StringLE()) 205 case PermissionGroup: 206 return json.Marshal(d.Group().StringCompressed()) 207 default: 208 return []byte(`"*"`), nil 209 } 210 } 211 212 // UnmarshalJSON implements the json.Unmarshaler interface. 213 func (d *PermissionDesc) UnmarshalJSON(data []byte) error { 214 var s string 215 if err := json.Unmarshal(data, &s); err != nil { 216 return err 217 } 218 219 const uint160HexSize = 2 * util.Uint160Size 220 switch len(s) { 221 case 2 + uint160HexSize: 222 // allow to unmarshal both hex and 0xhex forms 223 if s[0] != '0' || s[1] != 'x' { 224 return errors.New("invalid uint160") 225 } 226 s = s[2:] 227 fallthrough 228 case uint160HexSize: 229 u, err := util.Uint160DecodeStringLE(s) 230 if err != nil { 231 return err 232 } 233 d.Type = PermissionHash 234 d.Value = u 235 return nil 236 case 66: 237 pub, err := keys.NewPublicKeyFromString(s) 238 if err != nil { 239 return err 240 } 241 d.Type = PermissionGroup 242 d.Value = pub 243 return nil 244 case 1: 245 if s == "*" { 246 d.Type = PermissionWildcard 247 return nil 248 } 249 } 250 return errors.New("unknown permission") 251 } 252 253 // ToStackItem converts PermissionDesc to stackitem.Item. 254 func (d *PermissionDesc) ToStackItem() stackitem.Item { 255 switch d.Type { 256 case PermissionWildcard: 257 return stackitem.Null{} 258 case PermissionHash: 259 return stackitem.NewByteArray(d.Hash().BytesBE()) 260 case PermissionGroup: 261 return stackitem.NewByteArray(d.Group().Bytes()) 262 default: 263 panic("unsupported PermissionDesc type") 264 } 265 } 266 267 // FromStackItem converts stackitem.Item to PermissionDesc. 268 func (d *PermissionDesc) FromStackItem(item stackitem.Item) error { 269 if _, ok := item.(stackitem.Null); ok { 270 d.Type = PermissionWildcard 271 return nil 272 } 273 if item.Type() != stackitem.ByteArrayT { 274 return fmt.Errorf("unsupported permission descriptor type: %s", item.Type()) 275 } 276 byteArr, err := item.TryBytes() 277 if err != nil { 278 return err 279 } 280 switch len(byteArr) { 281 case util.Uint160Size: 282 hash, _ := util.Uint160DecodeBytesBE(byteArr) 283 d.Type = PermissionHash 284 d.Value = hash 285 case 33: 286 pKey, err := keys.NewPublicKeyFromBytes(byteArr, elliptic.P256()) 287 if err != nil { 288 return err 289 } 290 d.Type = PermissionGroup 291 d.Value = pKey 292 default: 293 return errors.New("invalid ByteArray length") 294 } 295 return nil 296 } 297 298 // ToStackItem converts Permission to stackitem.Item. 299 func (p *Permission) ToStackItem() stackitem.Item { 300 var methods stackitem.Item 301 contract := p.Contract.ToStackItem() 302 if p.Methods.IsWildcard() { 303 methods = stackitem.Null{} 304 } else { 305 m := make([]stackitem.Item, len(p.Methods.Value)) 306 for i := range p.Methods.Value { 307 m[i] = stackitem.Make(p.Methods.Value[i]) 308 } 309 methods = stackitem.Make(m) 310 } 311 return stackitem.NewStruct([]stackitem.Item{ 312 contract, 313 methods, 314 }) 315 } 316 317 // FromStackItem converts stackitem.Item to Permission. 318 func (p *Permission) FromStackItem(item stackitem.Item) error { 319 var err error 320 if item.Type() != stackitem.StructT { 321 return errors.New("invalid Permission stackitem type") 322 } 323 str := item.Value().([]stackitem.Item) 324 if len(str) != 2 { 325 return errors.New("invalid Permission stackitem length") 326 } 327 desc := new(PermissionDesc) 328 err = desc.FromStackItem(str[0]) 329 if err != nil { 330 return fmt.Errorf("invalid Contract stackitem: %w", err) 331 } 332 p.Contract = *desc 333 if _, ok := str[1].(stackitem.Null); ok { 334 p.Methods = WildStrings{Value: nil} 335 } else { 336 if str[1].Type() != stackitem.ArrayT { 337 return errors.New("invalid Methods stackitem type") 338 } 339 methods := str[1].Value().([]stackitem.Item) 340 p.Methods = WildStrings{ 341 Value: make([]string, len(methods)), 342 } 343 for i := range methods { 344 p.Methods.Value[i], err = stackitem.ToString(methods[i]) 345 if err != nil { 346 return err 347 } 348 } 349 } 350 return nil 351 }