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  }