github.com/hyperledger/burrow@v0.34.5-0.20220512172541-77f09336001d/execution/native/contract.go (about)

     1  // Copyright Monax Industries Limited
     2  // SPDX-License-Identifier: Apache-2.0
     3  
     4  package native
     5  
     6  import (
     7  	"fmt"
     8  
     9  	"github.com/hyperledger/burrow/acm"
    10  	"github.com/hyperledger/burrow/acm/acmstate"
    11  	"github.com/hyperledger/burrow/crypto"
    12  	"github.com/hyperledger/burrow/execution/engine"
    13  	"github.com/hyperledger/burrow/execution/errors"
    14  	"github.com/hyperledger/burrow/execution/evm/abi"
    15  	"github.com/hyperledger/burrow/logging"
    16  )
    17  
    18  //
    19  // Native (go) contracts are dispatched based on account permissions and can access
    20  // and modify an account's permissions
    21  //
    22  
    23  // Instructions on adding an native function. First declare a function like so:
    24  //
    25  // func unsetBase(context Context, args unsetBaseArgs) (unsetBaseRets, error) {
    26  // }
    27  //
    28  // The name of the function will be used as the name of the function in solidity. The
    29  // first arguments is Context; this will give you access to state, and the logger
    30  // etc. The second arguments must be a struct type. The members of this struct must be
    31  // exported (start with uppercase letter), and they will be converted into arguments
    32  // for the solidity function, with the same types. The first return value is a struct
    33  // which defines the return values from solidity just like the arguments.
    34  //
    35  // The second return value must be error. If non-nil is returned for error, then
    36  // the current transaction will be aborted and the execution will stop.
    37  //
    38  // For each contract you will need to create a Contract{} struct,
    39  // with the function listed. Only the PermFlag and the function F needs to be filled
    40  // in for each Function. Add this to the SNativeContracts() function.
    41  
    42  // Contract is metadata for native contract. Acts as a call target
    43  // from the EVM. Can be used to generate bindings in a smart contract languages.
    44  type Contract struct {
    45  	// Comment describing purpose of native contract and reason for assembling
    46  	// the particular functions
    47  	Comment string
    48  	// Name of the native contract
    49  	Name          string
    50  	functionsByID map[abi.FunctionID]*Function
    51  	functions     []*Function
    52  	address       crypto.Address
    53  	logger        *logging.Logger
    54  }
    55  
    56  var _ engine.Native = &Contract{}
    57  
    58  // Create a new native contract description object by passing a comment, name
    59  // and a list of member functions descriptions
    60  func NewContract(name string, comment string, logger *logging.Logger, fs ...Function) (*Contract, error) {
    61  	address := engine.AddressFromName(name)
    62  	functionsByID := make(map[abi.FunctionID]*Function, len(fs))
    63  	functions := make([]*Function, len(fs))
    64  	logger = logger.WithScope("NativeContract")
    65  	for i, f := range fs {
    66  		function := f
    67  		err := function.init(address)
    68  		if err != nil {
    69  			return nil, err
    70  		}
    71  		if function.abi == nil {
    72  			return nil, fmt.Errorf("could not establish ABI for function - contract functions must have a " +
    73  				"struct second argument in order to establish ABI")
    74  		}
    75  		function.contractName = name
    76  		function.logger = logger
    77  		fid := function.abi.FunctionID
    78  		otherF, ok := functionsByID[fid]
    79  		if ok {
    80  			return nil, fmt.Errorf("function with ID %x already defined: %s", fid, otherF.Signature())
    81  		}
    82  		functionsByID[fid] = &function
    83  		functions[i] = &function
    84  	}
    85  	return &Contract{
    86  		Comment:       comment,
    87  		Name:          name,
    88  		functionsByID: functionsByID,
    89  		functions:     functions,
    90  		address:       address,
    91  		logger:        logger,
    92  	}, nil
    93  }
    94  
    95  // Dispatch is designed to be called from the EVM once a native contract
    96  // has been selected.
    97  func (c *Contract) Call(state engine.State, params engine.CallParams) (output []byte, err error) {
    98  	if len(params.Input) < abi.FunctionIDSize {
    99  		return nil, errors.Errorf(errors.Codes.NativeFunction,
   100  			"Burrow Native dispatch requires a 4-byte function identifier but arguments are only %v bytes long",
   101  			len(params.Input))
   102  	}
   103  
   104  	var id abi.FunctionID
   105  	copy(id[:], params.Input)
   106  	function, err := c.FunctionByID(id)
   107  	if err != nil {
   108  		return nil, err
   109  	}
   110  
   111  	params.Input = params.Input[abi.FunctionIDSize:]
   112  
   113  	return function.Call(state, params)
   114  }
   115  
   116  func (c *Contract) SetExternals(externals engine.Dispatcher) {
   117  	for _, f := range c.functions {
   118  		f.SetExternals(externals)
   119  	}
   120  }
   121  
   122  func (c *Contract) FullName() string {
   123  	return c.Name
   124  }
   125  
   126  // We define the address of an native contact as the last 20 bytes of the sha3
   127  // hash of its name
   128  func (c *Contract) Address() crypto.Address {
   129  	return c.address
   130  }
   131  
   132  // Get function by calling identifier FunctionSelector
   133  func (c *Contract) FunctionByID(id abi.FunctionID) (*Function, errors.CodedError) {
   134  	f, ok := c.functionsByID[id]
   135  	if !ok {
   136  		return nil,
   137  			errors.Errorf(errors.Codes.NativeFunction, "unknown native function with ID %x", id)
   138  	}
   139  	return f, nil
   140  }
   141  
   142  // Get function by name
   143  func (c *Contract) FunctionByName(name string) *Function {
   144  	for _, f := range c.functions {
   145  		if f.name == name {
   146  			return f
   147  		}
   148  	}
   149  	return nil
   150  }
   151  
   152  // Get functions in order of declaration
   153  func (c *Contract) Functions() []*Function {
   154  	functions := make([]*Function, len(c.functions))
   155  	copy(functions, c.functions)
   156  	return functions
   157  }
   158  
   159  func (c *Contract) ContractMeta() []*acm.ContractMeta {
   160  	// FIXME: make this return actual ABI metadata
   161  	metadata := "{}"
   162  	metadataHash := acmstate.GetMetadataHash(metadata)
   163  	return []*acm.ContractMeta{
   164  		{
   165  			CodeHash:     []byte(c.Name),
   166  			MetadataHash: metadataHash[:],
   167  			Metadata:     metadata,
   168  		},
   169  	}
   170  }