github.com/datachainlab/burrow@v0.25.0/execution/evm/snative.go (about) 1 // Copyright 2017 Monax Industries Limited 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package evm 16 17 import ( 18 "fmt" 19 "reflect" 20 21 "strings" 22 23 "github.com/hyperledger/burrow/acm" 24 "github.com/hyperledger/burrow/crypto" 25 "github.com/hyperledger/burrow/crypto/sha3" 26 "github.com/hyperledger/burrow/execution/errors" 27 "github.com/hyperledger/burrow/execution/evm/abi" 28 "github.com/hyperledger/burrow/logging" 29 "github.com/hyperledger/burrow/logging/structure" 30 "github.com/hyperledger/burrow/permission" 31 ) 32 33 // 34 // SNative (from 'secure natives') are native (go) contracts that are dispatched 35 // based on account permissions and can access and modify an account's permissions 36 // 37 38 // Metadata for SNative contract. Acts as a call target from the EVM. Can be 39 // used to generate bindings in a smart contract languages. 40 type SNativeContractDescription struct { 41 // Comment describing purpose of SNative contract and reason for assembling 42 // the particular functions 43 Comment string 44 // Name of the SNative contract 45 Name string 46 functionsByID map[abi.FunctionID]*SNativeFunctionDescription 47 functions []*SNativeFunctionDescription 48 } 49 50 // Metadata for SNative functions. Act as call targets for the EVM when 51 // collected into an SNativeContractDescription. Can be used to generate 52 // bindings in a smart contract languages. 53 type SNativeFunctionDescription struct { 54 // Comment describing function's purpose, parameters, and return value 55 Comment string 56 // Function name (used to form signature) 57 Name string 58 // Function arguments 59 Arguments reflect.Type 60 // Function return values 61 Returns reflect.Type 62 // The abi 63 Abi abi.FunctionSpec 64 // Permissions required to call function 65 PermFlag permission.PermFlag 66 // Native function to which calls will be dispatched when a containing 67 F func(stateWriter Interface, caller crypto.Address, gas *uint64, logger *logging.Logger, 68 v interface{}) (interface{}, error) 69 } 70 71 func registerSNativeContracts() { 72 for _, contract := range SNativeContracts() { 73 if !RegisterNativeContract(contract.Address(), contract.Dispatch) { 74 panic(fmt.Errorf("could not register SNative contract %s because address %s already registered", 75 contract.Address(), contract.Name)) 76 } 77 } 78 } 79 80 // Returns a map of all SNative contracts defined indexed by name 81 func SNativeContracts() map[string]*SNativeContractDescription { 82 contracts := []*SNativeContractDescription{ 83 NewSNativeContract(` 84 * Interface for managing Secure Native authorizations. 85 * @dev This interface describes the functions exposed by the SNative permissions layer in burrow. 86 `, 87 "Permissions", 88 &SNativeFunctionDescription{Comment: ` 89 * @notice Adds a role to an account 90 * @param Account account address 91 * @param Role role name 92 * @return result whether role was added 93 `, 94 Name: "addRole", 95 PermFlag: permission.AddRole, 96 Arguments: reflect.TypeOf(addRoleArgs{}), 97 Returns: reflect.TypeOf(addRoleRets{}), 98 F: addRole}, 99 100 &SNativeFunctionDescription{Comment: ` 101 * @notice Removes a role from an account 102 * @param Account account address 103 * @param Role role name 104 * @return result whether role was removed 105 `, 106 Name: "removeRole", 107 PermFlag: permission.RemoveRole, 108 Arguments: reflect.TypeOf(removeRoleArgs{}), 109 Returns: reflect.TypeOf(removeRoleRets{}), 110 F: removeRole}, 111 112 &SNativeFunctionDescription{Comment: ` 113 * @notice Indicates whether an account has a role 114 * @param Account account address 115 * @param Role role name 116 * @return result whether account has role 117 `, 118 Name: "hasRole", 119 PermFlag: permission.HasRole, 120 Arguments: reflect.TypeOf(hasRoleArgs{}), 121 Returns: reflect.TypeOf(hasRoleRets{}), 122 F: hasRole}, 123 124 &SNativeFunctionDescription{Comment: ` 125 * @notice Sets the permission flags for an account. Makes them explicitly set (on or off). 126 * @param Account account address 127 * @param Permission the base permissions flags to set for the account 128 * @param Set whether to set or unset the permissions flags at the account level 129 * @return The permission flag that was set as uint64 130 `, 131 Name: "setBase", 132 PermFlag: permission.SetBase, 133 Arguments: reflect.TypeOf(setBaseArgs{}), 134 Returns: reflect.TypeOf(setBaseRets{}), 135 F: setBase}, 136 137 &SNativeFunctionDescription{Comment: ` 138 * @notice Unsets the permissions flags for an account. Causes permissions being unset to fall through to global permissions. 139 * @param Account account address 140 * @param Permission the permissions flags to unset for the account 141 * @return The permission flag that was unset as uint64 142 `, 143 Name: "unsetBase", 144 PermFlag: permission.UnsetBase, 145 Arguments: reflect.TypeOf(unsetBaseArgs{}), 146 Returns: reflect.TypeOf(unsetBaseRets{}), 147 F: unsetBase}, 148 149 &SNativeFunctionDescription{Comment: ` 150 * @notice Indicates whether an account has a subset of permissions set 151 * @param Account account address 152 * @param Permission the permissions flags (mask) to check whether enabled against base permissions for the account 153 * @return result whether account has the passed permissions flags set 154 `, 155 Name: "hasBase", 156 PermFlag: permission.HasBase, 157 Arguments: reflect.TypeOf(hasBaseArgs{}), 158 Returns: reflect.TypeOf(hasBaseRets{}), 159 F: hasBase}, 160 161 &SNativeFunctionDescription{Comment: ` 162 * @notice Sets the global (default) permissions flags for the entire chain 163 * @param Permission the permissions flags to set 164 * @param Set whether to set (or unset) the permissions flags 165 * @return The permission flag that was set as uint64 166 `, 167 Name: "setGlobal", 168 PermFlag: permission.SetGlobal, 169 Arguments: reflect.TypeOf(setGlobalArgs{}), 170 Returns: reflect.TypeOf(setGlobalRets{}), 171 F: setGlobal}, 172 ), 173 } 174 175 contractMap := make(map[string]*SNativeContractDescription, len(contracts)) 176 for _, contract := range contracts { 177 if _, ok := contractMap[contract.Name]; ok { 178 // If this happens we have a pseudo compile time error that will be caught 179 // on native.go init() 180 panic(fmt.Errorf("duplicate contract with name %s defined. "+ 181 "Contract names must be unique", contract.Name)) 182 } 183 contractMap[contract.Name] = contract 184 } 185 return contractMap 186 } 187 188 // Create a new SNative contract description object by passing a comment, name 189 // and a list of member functions descriptions 190 func NewSNativeContract(comment, name string, 191 functions ...*SNativeFunctionDescription) *SNativeContractDescription { 192 193 functionsByID := make(map[abi.FunctionID]*SNativeFunctionDescription, len(functions)) 194 for _, f := range functions { 195 196 f.Abi = *abi.SpecFromStructReflect(f.Name, f.Arguments, f.Returns) 197 fid := f.Abi.FunctionID 198 otherF, ok := functionsByID[fid] 199 if ok { 200 panic(fmt.Errorf("function with ID %x already defined: %s", fid, otherF.Signature())) 201 } 202 functionsByID[fid] = f 203 } 204 return &SNativeContractDescription{ 205 Comment: comment, 206 Name: name, 207 functionsByID: functionsByID, 208 functions: functions, 209 } 210 } 211 212 // This function is designed to be called from the EVM once a SNative contract 213 // has been selected. It is also placed in a registry by registerSNativeContracts 214 // So it can be looked up by SNative address 215 func (contract *SNativeContractDescription) Dispatch(st Interface, caller crypto.Address, 216 args []byte, gas *uint64, logger *logging.Logger) (output []byte, err error) { 217 218 logger = logger.With(structure.ScopeKey, "Dispatch", "contract_name", contract.Name) 219 220 if len(args) < abi.FunctionIDSize { 221 return nil, errors.ErrorCodef(errors.ErrorCodeNativeFunction, 222 "SNatives dispatch requires a 4-byte function identifier but arguments are only %v bytes long", 223 len(args)) 224 } 225 226 var id abi.FunctionID 227 copy(id[:], args) 228 function, err := contract.FunctionByID(id) 229 if err != nil { 230 return nil, err 231 } 232 233 logger.TraceMsg("Dispatching to function", 234 "caller", caller, 235 "function_name", function.Name) 236 237 remainingArgs := args[abi.FunctionIDSize:] 238 239 // check if we have permission to call this function 240 if !HasPermission(st, caller, function.PermFlag) { 241 return nil, errors.LacksSNativePermission{Address: caller, SNative: function.Name} 242 } 243 244 nativeArgs := reflect.New(function.Arguments).Interface() 245 err = abi.UnpackIntoStruct(function.Abi.Inputs, remainingArgs, nativeArgs) 246 if err != nil { 247 return nil, err 248 } 249 250 nativeRets, err := function.F(st, caller, gas, logger, nativeArgs) 251 if err != nil { 252 return nil, err 253 } 254 err = st.Error() 255 if err != nil { 256 return nil, fmt.Errorf("state error in %v: %v", function, err) 257 } 258 259 return abi.PackIntoStruct(function.Abi.Outputs, nativeRets) 260 } 261 262 // We define the address of an SNative contact as the last 20 bytes of the sha3 263 // hash of its name 264 func (contract *SNativeContractDescription) Address() (address crypto.Address) { 265 hash := sha3.Sha3([]byte(contract.Name)) 266 copy(address[:], hash[len(hash)-crypto.AddressLength:]) 267 return 268 } 269 270 // Get function by calling identifier FunctionSelector 271 func (contract *SNativeContractDescription) FunctionByID(id abi.FunctionID) (*SNativeFunctionDescription, errors.CodedError) { 272 f, ok := contract.functionsByID[id] 273 if !ok { 274 return nil, 275 errors.ErrorCodef(errors.ErrorCodeNativeFunction, "unknown SNative function with ID %x", id) 276 } 277 return f, nil 278 } 279 280 // Get function by name 281 func (contract *SNativeContractDescription) FunctionByName(name string) (*SNativeFunctionDescription, error) { 282 for _, f := range contract.functions { 283 if f.Name == name { 284 return f, nil 285 } 286 } 287 return nil, fmt.Errorf("unknown SNative function with name %s", name) 288 } 289 290 // Get functions in order of declaration 291 func (contract *SNativeContractDescription) Functions() []*SNativeFunctionDescription { 292 functions := make([]*SNativeFunctionDescription, len(contract.functions)) 293 copy(functions, contract.functions) 294 return functions 295 } 296 297 // 298 // SNative functions 299 // 300 301 // Get function signature 302 func (function *SNativeFunctionDescription) Signature() string { 303 argTypeNames := make([]string, len(function.Abi.Inputs)) 304 for i, arg := range function.Abi.Inputs { 305 argTypeNames[i] = arg.EVM.GetSignature() 306 } 307 return fmt.Sprintf("%s(%s)", function.Name, 308 strings.Join(argTypeNames, ",")) 309 } 310 311 // Get number of function arguments 312 func (function *SNativeFunctionDescription) NArgs() int { 313 return len(function.Abi.Inputs) 314 } 315 316 func (fn *SNativeFunctionDescription) String() string { 317 return fmt.Sprintf("SNativeFunction{Name: %s; Inputs: %d; Outputs: %d}", 318 fn.Name, len(fn.Abi.Inputs), len(fn.Abi.Outputs)) 319 } 320 321 // Permission function defintions 322 323 // TODO: catch errors, log em, return 0s to the vm (should some errors cause exceptions though?) 324 type hasBaseArgs struct { 325 Account crypto.Address 326 Permission uint64 327 } 328 329 type hasBaseRets struct { 330 Result bool 331 } 332 333 func hasBase(state Interface, caller crypto.Address, gas *uint64, logger *logging.Logger, 334 a interface{}) (interface{}, error) { 335 args := a.(*hasBaseArgs) 336 337 if !state.Exists(args.Account) { 338 return false, fmt.Errorf("unknown account %s", args.Account) 339 } 340 permN := permission.PermFlag(args.Permission) // already shifted 341 if !permN.IsValid() { 342 return false, permission.ErrInvalidPermission(permN) 343 } 344 hasPermission := HasPermission(state, args.Account, permN) 345 logger.Trace.Log("function", "hasBase", 346 "address", args.Account.String(), 347 "perm_flag", fmt.Sprintf("%b", permN), 348 "has_permission", hasPermission) 349 return hasBaseRets{Result: hasPermission}, nil 350 } 351 352 type setBaseArgs struct { 353 Account crypto.Address 354 Permission uint64 355 Set bool 356 } 357 358 type setBaseRets struct { 359 Result uint64 360 } 361 362 func setBase(stateWriter Interface, caller crypto.Address, gas *uint64, 363 logger *logging.Logger, a interface{}) (interface{}, error) { 364 args := a.(*setBaseArgs) 365 366 exists := stateWriter.Exists(args.Account) 367 if !exists { 368 return false, fmt.Errorf("unknown account %s", args.Account) 369 } 370 permN := permission.PermFlag(args.Permission) 371 if !permN.IsValid() { 372 return 0, permission.ErrInvalidPermission(permN) 373 } 374 stateWriter.SetPermission(args.Account, permN, args.Set) 375 logger.Trace.Log("function", "setBase", "address", args.Account.String(), 376 "permission_flag", fmt.Sprintf("%b", permN), 377 "permission_value", args.Permission) 378 return setBaseRets{Result: uint64(permN)}, nil 379 } 380 381 type unsetBaseArgs struct { 382 Account crypto.Address 383 Permission uint64 384 } 385 386 type unsetBaseRets struct { 387 Result uint64 388 } 389 390 func unsetBase(stateWriter Interface, caller crypto.Address, gas *uint64, logger *logging.Logger, 391 a interface{}) (r interface{}, err error) { 392 args := a.(*unsetBaseArgs) 393 394 if !stateWriter.Exists(args.Account) { 395 return false, fmt.Errorf("unknown account %s", args.Account) 396 } 397 permN := permission.PermFlag(args.Permission) 398 if !permN.IsValid() { 399 return 0, permission.ErrInvalidPermission(permN) 400 } 401 stateWriter.UnsetPermission(args.Account, permN) 402 logger.Trace.Log("function", "unsetBase", "address", args.Account.String(), 403 "perm_flag", fmt.Sprintf("%b", permN), 404 "permission_flag", fmt.Sprintf("%b", permN)) 405 406 return unsetBaseRets{Result: uint64(permN)}, nil 407 } 408 409 type setGlobalArgs struct { 410 Permission uint64 411 Set bool 412 } 413 414 type setGlobalRets struct { 415 Result uint64 416 } 417 418 func setGlobal(stateWriter Interface, caller crypto.Address, gas *uint64, 419 logger *logging.Logger, a interface{}) (interface{}, error) { 420 421 args := a.(*setGlobalArgs) 422 423 permN := permission.PermFlag(args.Permission) 424 if !permN.IsValid() { 425 return 0, permission.ErrInvalidPermission(permN) 426 } 427 stateWriter.SetPermission(acm.GlobalPermissionsAddress, permN, args.Set) 428 logger.Trace.Log("function", "setGlobal", 429 "permission_flag", fmt.Sprintf("%b", permN), 430 "permission_value", args.Set) 431 return setGlobalRets{Result: uint64(permN)}, nil 432 } 433 434 type hasRoleArgs struct { 435 Account crypto.Address 436 Role string 437 } 438 439 type hasRoleRets struct { 440 Result bool 441 } 442 443 func hasRole(st Interface, caller crypto.Address, gas *uint64, 444 logger *logging.Logger, a interface{}) (interface{}, error) { 445 446 args := a.(*hasRoleArgs) 447 perms := st.GetPermissions(args.Account) 448 if err := st.Error(); err != nil { 449 return false, fmt.Errorf("hasRole could not get permissions: %v", err) 450 } 451 hasRole := perms.HasRole(args.Role) 452 logger.Trace.Log("function", "hasRole", "address", args.Account.String(), 453 "role", args.Role, 454 "has_role", hasRole) 455 return hasRoleRets{Result: hasRole}, nil 456 } 457 458 type addRoleArgs struct { 459 Account crypto.Address 460 Role string 461 } 462 463 type addRoleRets struct { 464 Result bool 465 } 466 467 func addRole(stateWriter Interface, caller crypto.Address, gas *uint64, logger *logging.Logger, 468 v interface{}) (interface{}, error) { 469 args := v.(*addRoleArgs) 470 roleAdded := stateWriter.AddRole(args.Account, args.Role) 471 logger.Trace.Log("function", "addRole", "address", args.Account.String(), 472 "role", args.Role, 473 "role_added", roleAdded) 474 return addRoleRets{Result: roleAdded}, nil 475 } 476 477 type removeRoleArgs struct { 478 Account crypto.Address 479 Role string 480 } 481 482 type removeRoleRets struct { 483 Result bool 484 } 485 486 func removeRole(stateWriter Interface, caller crypto.Address, gas *uint64, logger *logging.Logger, 487 a interface{}) (interface{}, error) { 488 args := a.(*removeRoleArgs) 489 490 roleRemoved := stateWriter.RemoveRole(args.Account, args.Role) 491 logger.Trace.Log("function", "removeRole", "address", args.Account.String(), 492 "role", args.Role, 493 "role_removed", roleRemoved) 494 return removeRoleRets{Result: roleRemoved}, nil 495 }