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 }