github.com/hyperledger/burrow@v0.34.5-0.20220512172541-77f09336001d/execution/engine/call.go (about) 1 package engine 2 3 import ( 4 "math/big" 5 6 "github.com/hyperledger/burrow/execution/errors" 7 "github.com/hyperledger/burrow/execution/exec" 8 "github.com/hyperledger/burrow/permission" 9 ) 10 11 var big64 = big.NewInt(64) 12 13 // Call provides a standard wrapper for implementing Callable.Call with appropriate error handling and event firing. 14 func Call(state State, params CallParams, execute func(State, CallParams) ([]byte, error)) ([]byte, error) { 15 maybe := new(errors.Maybe) 16 if params.CallType == exec.CallTypeCall || params.CallType == exec.CallTypeCode { 17 // NOTE: Delegate and Static CallTypes do not transfer the value to the callee. 18 maybe.PushError(Transfer(state.CallFrame, params.Caller, params.Callee, ¶ms.Value)) 19 } 20 21 output := maybe.Bytes(execute(state, params)) 22 // fire the post call event (including exception if applicable) and make sure we return the accumulated call error 23 maybe.PushError(FireCallEvent(state.CallFrame, maybe.Error(), state.EventSink, output, params)) 24 return output, maybe.Error() 25 } 26 27 func FireCallEvent(callFrame *CallFrame, callErr error, eventSink exec.EventSink, output []byte, 28 params CallParams) error { 29 // fire the post call event (including exception if applicable) 30 return eventSink.Call(&exec.CallEvent{ 31 CallType: params.CallType, 32 CallData: &exec.CallData{ 33 Caller: params.Caller, 34 Callee: params.Callee, 35 Data: params.Input, 36 Value: params.Value.Bytes(), 37 Gas: params.Gas.Bytes(), 38 }, 39 Origin: params.Origin, 40 StackDepth: callFrame.CallStackDepth(), 41 Return: output, 42 }, errors.AsException(callErr)) 43 } 44 45 func CallFromSite(st State, dispatcher Dispatcher, site CallParams, target CallParams) ([]byte, error) { 46 err := EnsurePermission(st.CallFrame, site.Callee, permission.Call) 47 if err != nil { 48 return nil, err 49 } 50 // Get the arguments from the memory 51 // EVM contract 52 err = UseGasNegative(site.Gas, GasGetAccount) 53 if err != nil { 54 return nil, err 55 } 56 // since CALL is used also for sending funds, 57 // acc may not exist yet. This is an errors.CodedError for 58 // CALLCODE, but not for CALL, though I don't think 59 // ethereum actually cares 60 acc, err := st.CallFrame.GetAccount(target.Callee) 61 if err != nil { 62 return nil, err 63 } 64 if acc == nil { 65 if target.CallType != exec.CallTypeCall { 66 return nil, errors.Codes.UnknownAddress 67 } 68 // We're sending funds to a new account so we must create it first 69 err := st.CallFrame.CreateAccount(site.Callee, target.Callee) 70 if err != nil { 71 return nil, err 72 } 73 acc, err = st.CallFrame.GetAccount(target.Callee) 74 if err != nil { 75 return nil, err 76 } 77 } 78 79 // Establish a stack frame and perform the call 80 childCallFrame, err := st.CallFrame.NewFrame() 81 if err != nil { 82 return nil, err 83 } 84 childState := State{ 85 CallFrame: childCallFrame, 86 Blockchain: st.Blockchain, 87 EventSink: st.EventSink, 88 } 89 // Ensure that gasLimit is reasonable 90 if site.Gas.Cmp(target.Gas) < 0 { 91 // EIP150 - the 63/64 rule - rather than errors.CodedError we pass this specified fraction of the total available gas 92 gas := new(big.Int) 93 target.Gas.Sub(site.Gas, gas.Div(site.Gas, big64)) 94 } 95 // NOTE: we will return any used gas later. 96 site.Gas.Sub(site.Gas, target.Gas) 97 98 // Setup callee params for call type 99 target.Origin = site.Origin 100 101 // Set up the caller/callee context 102 switch target.CallType { 103 case exec.CallTypeCall: 104 // Calls contract at target from this contract normally 105 // Value: transferred 106 // Caller: this contract 107 // Storage: target 108 // Code: from target 109 target.Caller = site.Callee 110 111 case exec.CallTypeStatic: 112 // Calls contract at target from this contract with no state mutation 113 // Value: not transferred 114 // Caller: this contract 115 // Storage: target (read-only) 116 // Code: from target 117 target.Caller = site.Callee 118 119 childState.CallFrame.ReadOnly() 120 childState.EventSink = exec.NewLogFreeEventSink(childState.EventSink) 121 122 case exec.CallTypeCode: 123 // Calling this contract from itself as if it had the code at target 124 // Value: transferred 125 // Caller: this contract 126 // Storage: this contract 127 // Code: from target 128 129 target.Caller = site.Callee 130 target.Callee = site.Callee 131 132 case exec.CallTypeDelegate: 133 // Calling this contract from the original caller as if it had the code at target 134 // Value: not transferred 135 // Caller: original caller 136 // Storage: this contract 137 // Code: from target 138 139 target.Caller = site.Caller 140 target.Callee = site.Callee 141 142 default: 143 // Switch should be exhaustive so we should reach this 144 panic("invalid call type") 145 } 146 147 dispatch := dispatcher.Dispatch(acc) 148 if dispatch == nil { 149 return nil, errors.Errorf(errors.Codes.NotCallable, "cannot call: %v", acc.Address) 150 } 151 returnData, err := dispatch.Call(childState, target) 152 153 if err == nil { 154 // Sync error is a hard stop 155 err = childState.CallFrame.Sync() 156 } 157 158 // Handle remaining gas. 159 //site.Gas.Add(site.Gas, target.Gas) 160 return returnData, err 161 }